perl.gg / secret-operators

<!-- category: secret-operators -->

The Eskimo Greeting }{

2026-03-22

Count the lines in a file:
perl -lne '}{ print $.'
Four characters do the work: }{

The Eskimo Greeting. Named because it looks like two people rubbing noses, Inuit style. One of Perl's "secret operators," a syntactic accident that turns out to be ridiculously useful in one-liners.

Those two braces break out of the implicit while loop that -n creates, and start a new block that runs exactly once, after all input has been processed. An END block without typing END.

Part 1: WHAT -n AND -p DO

When you write:
perl -ne 'CODE'
Perl wraps your code like this:
while (<>) { CODE }
Every line of input becomes $_, your code runs, next line. The -p flag does the same thing but adds print at the end of each iteration.

Simple enough. But what if you want to do something AFTER all lines are processed? Print a total. Show a summary. Output the final state of some accumulator.

You could use an END block:

perl -ne '$sum += $_; END { print $sum }' numbers.txt
Or you could use two braces and save yourself some typing.

Part 2: HOW }{ WORKS

perl -ne '$sum += $_; }{ print $sum' numbers.txt
Perl wraps it into:
while (<>) { $sum += $_; } { print $sum }
The } closes the while loop. The { opens a bare block. That bare block runs once, after the loop finishes. Your summary code goes there.
}{ / \ } { / \ close open while bare loop block Two Inuit, nose to nose
It's not a real operator. It's just how Perl's syntax shakes out when you jam a closing brace against an opening brace inside -n's implicit loop. A happy accident of grammar.

Part 3: LINE COUNTING

The simplest use. Count lines in a file:
perl -lne '}{ print $.' file.txt
$. is Perl's line counter. It auto-increments with every line read. After the Eskimo Greeting, the while loop is done, and $. holds the final count.

No code in the loop body at all. The -n loop just reads and discards every line. The }{ skips to the end where we print.

Equivalent to wc -l, but in Perl.

Part 4: SUMMING AND AVERAGING

Add up numbers, one per line:
perl -lne '$s += $_; }{ print $s' numbers.txt
The loop body accumulates. The Eskimo block prints the total.

Average:

perl -lne '$s += $_; }{ print $s / $.' numbers.txt
$s is the sum, $. is the count. Divide after all input is consumed. You can't compute the average until you've seen every value, which is exactly what }{ gives you.

Min and max:

perl -lne ' $min = $_ if !defined $min || $_ < $min; $max = $_ if !defined $max || $_ > $max; }{ print "min=$min max=$max" ' numbers.txt

Part 5: COMBINING WITH BEGIN

You can use BEGIN blocks in one-liners too. Now you have setup, loop, and teardown:
perl -lne ' BEGIN { print "Starting analysis..." } $total += $_; $count++; }{ printf "Lines: %d Sum: %d Avg: %.2f\n", $count, $total, $total / $count ' numbers.txt
Which Perl wraps into:
BEGIN { print "Starting analysis..." } while (<>) { $total += $_; $count++; } { printf "Lines: %d Sum: %d Avg: %.2f\n", $count, $total, $total / $count; }
Three phases. One command. No script file needed.

Part 6: WORD FREQUENCY AND LOG ANALYSIS

Count every word in a file, sorted by frequency:
perl -lne ' $w{lc $_}++ for split m~\W+~; }{ printf "%6d %s\n", $w{$_}, $_ for sort { $w{$b} <=> $w{$a} } keys %w ' book.txt
The loop body splits each line into words and counts them in a hash. The Eskimo block sorts by frequency and prints. A full word-frequency analyzer in one command.

IP address frequency from an access log:

perl -lne ' if (m~(\d+\.\d+\.\d+\.\d+)~) { $ip{$1}++ } }{ printf "%6d %s\n", $ip{$_}, $_ for sort { $ip{$b} <=> $ip{$a} } keys %ip ' access.log
Error and warning counts:
perl -lne ' $err++ if m~ERROR~; $warn++ if m~WARN~; }{ print "Errors: $err, Warnings: $warn" ' app.log
The loop extracts. The Eskimo block reports. Clean separation.

Part 7: MULTIPLE ACCUMULATORS

You're not limited to one variable. Build as many accumulators as you need:
perl -lne ' $lines++; $words += scalar split m~\s+~; $chars += length; }{ printf "lines: %d words: %d chars: %d\n", $lines, $words, $chars ' file.txt
A full wc clone. Lines, words, and characters.

HTTP status code breakdown:

perl -lne ' m~^(\d{3})~ && $code{$1}++; }{ printf "%s: %d\n", $_, $code{$_} for sort keys %code ' status_codes.txt
Four counters, one pass, one report.

Part 8: WITH -p (AND WHY YOU PROBABLY WANT -n)

The -p flag adds a print after your code. With }{:
perl -pe '}{ print "--- END ---\n"'
Becomes:
while (<>) { # (empty, nothing before }{) } { print "--- END ---\n"; print; # the implicit print from -p }
That implicit print still fires in the Eskimo block. And $_ is whatever it was last set to. Surprise.

Usually you want -n, not -p, when using }{. With -n, there's no ghost print at the end.

Part 9: GOTCHAS

Variables don't reset between files.

If you pass multiple files:

perl -lne '$c++; }{ print $c' file1.txt file2.txt
You get the total across both files. The }{ block runs once, after ALL input from ALL files. For per-file summaries, use eof instead:
perl -lne '$c++; if (eof) { print "$ARGV: $c"; $c = 0 }' file1.txt file2.txt
The eof function (without parens) returns true at the end of each file. Per-file hooks without the Eskimo.

$. counts across files too. After reading file1.txt (10 lines) and file2.txt (5 lines), $. is 15. Track per-file counts manually.

Empty input still runs the Eskimo block.

echo -n | perl -lne '$c++; }{ print "count: ", $c // 0'
No input lines, but the post-loop block executes. Accumulators are undef unless initialized. The // 0 handles it with defined-or.
.--. |o_o | "Two noses, one result." |:_/ | // \ \ (| | ) /'\_ _/`\ \___)=(___/

Part 10: }{ VS END BLOCK AND THE NAME

Both run code after all input. The differences are subtle.

END blocks run during interpreter shutdown. Global destruction may have started. Objects might be partially destroyed.

}{ runs as regular code after the loop. Everything is still alive. No destruction. For one-liners with objects or tied variables, }{ is the safer choice.

As for the name: Eskimo Greeting. Two Inuit people touching noses.

}{ } { \ / \ / \/ nose touch
Tilt your head. The } is one person facing right. The { is another facing left. They meet in the middle, noses touching. The name comes from Philippe Bruhat (BooK) and the Perl secret operators list. Same crew that named the goatse operator, the spaceship, and the Venus. Perl hackers have a talent for naming things.

Whatever you call it, two braces that turn any accumulating one-liner into a proper extract-and-report pipeline.

}{ / \ ____/ \____ / \ / \ | LOOP | | POST | | | | | | body | | code | \____/ \____/ End the loop. Begin the summary.
perl.gg