◈ Netsurfer / perl.gg / Maori Farewell

Maori Farewell

2024-07-07

This page explains the Maori Farewell trick, which gives you a BEGIN block inside a Perl one-liner. The butterfly operator handles cleanup at the end, but this trick handles setup at the start.
-M5;
That's it. The -M switch, the number 5, and a semicolon. Everything after the semicolon runs before your main code.

Named for the Maori people of New Zealand (the pattern continues the cultural naming tradition of the Eskimo greeting). It exploits -M in a way the designers probably didn't intend.

Part 1: THE TRICK

The -M switch normally loads a Perl module:
perl -MJSON -e 'print encode_json({a=>1})'
But with -M5;, something different happens:
perl -M'5;print "HELLO\n"' -ne 'print' file.txt
The command produces this output:
HELLO line 1 from file line 2 from file ...
The print happens BEFORE the file processing starts. It acts as a pseudo-BEGIN block hiding inside the -M switch.

Part 2: HOW IT WORKS

When Perl sees -M5;, it interprets this as:
use 5; # Require Perl version 5 (always true) print "..."; # Execute this code
The semicolon separates the version requirement from arbitrary code. Since we are all running Perl 5 or newer, the version check passes silently.

What comes after the semicolon runs at compile time, before the -n or -p loop even starts. It is not quite a real BEGIN block, but it acts like one for practical purposes.

.--. |o_o | |:_/ | // \ \ (| | ) /'\_ _/`\ \___)=(___/

Part 3: PRACTICAL EXAMPLES

Print a timestamp before processing:
perl -M'5;print scalar localtime, "\n"' -ne 'print if /error/i' log.txt
The command produces this output:
Wed Jan 5 15:30:45 2025 [15:30:46] ERROR: Database connection failed [15:31:02] ERROR: Invalid user input
The timestamp prints first, then matching lines follow.

Set a custom record separator:

perl -M'5;$/="\n\n"' -ne 'print if length > 100' document.txt
This sets paragraph mode BEFORE the loop starts. Each "line" is now actually a paragraph (text between blank lines).

Initialize a counter with a starting value:

perl -M'5;$n=100' -lne 'print $n++, ": $_"' file.txt
The command produces this output:
100: first line 101: second line 102: third line
The counter starts at 100 instead of 0.

Part 4: WHY NOT JUST USE BEGIN?

You might think:
perl -ne 'BEGIN { print "hello\n" } print' file.txt
And you would be right. That works too. But compare the character counts:
-M'5;print "hi"' 17 characters 'BEGIN{print "hi"}' 19 characters
In code golf, every character matters. The Maori Farewell saves two.

It also feels different because the initialization sits visually apart from the main loop code, which some find cleaner:

perl -M'5;$/=undef' -M'5;$verbose=1' -ne '...'
You can stack multiple -M flags, and each one handles one setup task. The command reads like a config file.

Part 5: VERSION FEATURES

Here is a bonus trick. You can use a real version number to enable language features:
perl -M5.010 -E 'say "hello"'
The -M5.010 flag enables Perl 5.10 features, including say. You can combine it with the semicolon trick:
perl -M'5.010;$count=0' -nE 'say ++$count, ": $_"' file.txt
You get both version features and initialization code in a single flag.

Common version features:

5.010 say, state, given/when 5.014 /r modifier for non-destructive substitution 5.016 __SUB__ for anonymous recursion 5.020 Postfix dereferencing

Part 6: LIMITATIONS

The Maori Farewell does not work with module import lists:
# THIS FAILS: perl -M"List::Util=sum;print 'hi'" -e '...'
The = syntax for selective imports breaks the trick. If you need module functions, load the module normally:
perl -MList::Util=sum -M'5;print "hi\n"' -e '...'
You use two -M flags instead. One flag loads the module, and the second flag runs the farewell.

Part 7: COMBINING WITH BUTTERFLY

Now you have both ends covered:
perl -M'5;print "START\n"' -lne '$sum+=$_ }{ print "Sum: $sum"' nums.txt
The command produces this output:
START Sum: 150
The Maori Farewell handles the beginning. The butterfly operator handles the end. Your one-liner now has a proper setup and teardown.

Part 8: THE ANNOTATED VERSION

perl -M'5;print "START\n"' -lne '$sum += $_ }{ print "END: $sum"' file
Breaking it down:
PIECE WHAT IT DOES ------------------------ ---------------------------------- perl Run Perl -M'5;print "START\n"' Maori Farewell: runs before loop -l Auto-chomp input, add newlines -n Wrap code in while(<>) loop -e '...' The main code $sum += $_ Loop body: accumulate sum }{ Butterfly: end loop, start end block print "END: $sum" End block: print final summary file Input file
You can chain three tricks into one command:
1. Maori Farewell for setup 2. -n for implicit loop 3. Butterfly for cleanup

Part 9: REAL-WORLD USES

Print column headers before CSV processing:
perl -M'5;print "Line,Content\n"' -lne 'print "$.,\"$_\""' data.txt
Set environment variables before processing:
perl -M'5;$ENV{DEBUG}=1' -ne '...' file.txt
Initialize complex data structures:
perl -M'5;%seen=();@order=()' -lne ' push @order, $_ unless $seen{$_}++; }{ print for @order ' file.txt
That last example is a unique-lines-in-order filter with explicit initialization of the tracking structures.

Part 10: THE NAME

Why "Maori Farewell"? It continues the cultural greeting theme:
}{ Eskimo greeting (two noses touching) -M5; Maori farewell (hongi - pressing foreheads)
Both names reference traditional greetings from indigenous peoples. The Perl community has a habit of using whimsical names.

Whether the cultural references are appropriate is debatable. The names stuck because they are memorable, and that matters for tribal knowledge passed between Perl hackers.

.---. / \ | START | \ / '---' | V .-------. / LOOP \ \ -n -p / '-------' | V .---. / \ | END | \ / '---' Maori at the top, Butterfly at the bottom
perl.gg
✓ Document: Doneone-liners / 12.1 KiB▧