╔╗ ╦ ╦╔╦╗╔╦╗╔═╗╦═╗╔═╗╦ ╦ ╦ ╠╩╗║ ║ ║ ║ ║╣ ╠╦╝╠╣ ║ ╚╦╝ ╚═╝╚═╝ ╩ ╩ ╚═╝╩╚═╚ ╩═╝ ╩ ╔═╗╔═╗╔═╗╦═╗╔═╗╔╦╗╔═╗╦═╗ ║ ║╠═╝║╣ ╠╦╝╠═╣ ║ ║ ║╠╦╝ ╚═╝╩ ╚═╝╩╚═╩ ╩ ╩ ╚═╝╩╚═ ============================================================================ Two characters. That's all it takes to turn a per-line loop into a summarizer: }{ The butterfly operator. Also called the Eskimo greeting (turn your head sideways, it's two people rubbing noses). Discovered by Abigail in 1997 after reading about Perl's -n and -p switches. It's not really an operator. It's an exploit. And it only works in one-liners. ============================================================================ PART 1: THE TRICK ----------------- When you run: perl -lne '$sum += $_ }{ print $sum' numbers.txt Perl actually generates this code: LINE: while (defined($_ = )) { chomp; $sum += $_; } { print $sum; } See what happened? The }{ split the generated while loop in half. The first part runs for every line. The second part runs once, at the end. It's a makeshift END block hidden inside a one-liner. ============================================================================ PART 2: HOW IT WORKS -------------------- The -n switch wraps your code in: LINE: while (defined($_ = )) { YOUR_CODE_HERE } When your code contains }{, it becomes: LINE: while (defined($_ = )) { code_before } # <-- The } closes the while loop { # <-- The { starts a bare block code_after } # <-- Perl's implicit closing brace The while loop finishes. Then the bare block runs. Once. .--. |o_o | |:_/ | // \ \ (| | ) /'\_ _/`\ \___)=(___/ ============================================================================ PART 3: WHY ONE-LINERS ONLY --------------------------- Try putting }{ in a regular script: #!/usr/bin/env perl $sum += $_ }{ print $sum; # SYNTAX ERROR Perl chokes. There's no implicit while loop to close. The butterfly only works because -n and -p generate code that contains a brace waiting to be matched. Without that generated structure, }{ is just a confused pair of brackets. ============================================================================ PART 4: CLASSIC EXAMPLES ------------------------ Sum all numbers in a file: perl -lne '$sum += $_ }{ print $sum' numbers.txt How it works: * Each line: add to $sum * After all lines: print total Find the longest line: perl -lne '$max = length if length > $max }{ print $max' file.txt How it works: * Each line: update $max if current line is longer * After all lines: print the maximum Count non-empty lines: perl -lne '$count++ if /\S/ }{ print $count' file.txt How it works: * Each line: increment if it contains non-whitespace * After all lines: print the count Average of numbers: perl -lne '$sum += $_; $n++ }{ print $sum/$n' numbers.txt How it works: * Each line: accumulate sum and count * After all lines: divide for average ============================================================================ PART 5: THE ANATOMY ------------------- Every butterfly one-liner has this structure: perl -lne 'LOOP_CODE }{ END_CODE' file LOOP_CODE Runs for each line (accumulate, filter, count) }{ The butterfly - splits loop from summary END_CODE Runs once after all input (print, report, summarize) The -l switch auto-chomps input and adds newlines to output. Almost always what you want with }{. ============================================================================ PART 6: WITH -p INSTEAD OF -n ----------------------------- The -p switch also prints each line after processing. Combined with }{, you get both transformation AND summary: perl -lpe '$sum += $_ }{ print "Total: $sum"' numbers.txt Output: 5 10 15 Total: 30 Each number prints as-is, then the summary appears at the end. You can even suppress the per-line output by clearing $_: perl -lpe '$sum += $_; $_ = "" }{ print "Total: $sum"' numbers.txt Output: Total: 30 Same result as -n, but using -p's machinery. ============================================================================ PART 7: MULTIPLE SUMMARIES -------------------------- Nothing stops you from doing complex end-of-file logic: perl -lne ' $sum += $_; $count++; $min = $_ if !defined $min || $_ < $min; $max = $_ if !defined $max || $_ > $max; }{ print "Count: $count"; print "Sum: $sum"; print "Min: $min"; print "Max: $max"; print "Avg: " . ($sum/$count); ' numbers.txt Full statistical summary in a "one-liner" (okay, it's wrapped for readability, but it's still one perl command). ============================================================================ PART 8: THE NAME ---------------- Why "butterfly"? Look at }{ and tilt your head: }{ ^ / \ / \ | | / \ Wings spread. Antennae up. It's a stretch, but Perl folks love their whimsical names. The "Eskimo greeting" name comes from rotating it 90 degrees: } { Two faces touching noses. Traditional Inuit greeting. Also a stretch. Both names stuck because they're memorable. Nobody wants to say "the implicit loop terminator with trailing bare block." ============================================================================ PART 9: REAL-WORLD USES ----------------------- Word frequency count: perl -lne '$h{$_}++ for split /\s+/ }{ print "$_: $h{$_}" for sort keys %h' file.txt Unique line count: perl -lne '$seen{$_}++ }{ print scalar keys %seen' file.txt First and last line: perl -lne '$first //= $_; $last = $_ }{ print "First: $first\nLast: $last"' file.txt File size in lines and bytes: perl -lne '$bytes += length; }{ print "Lines: $.\nBytes: $bytes"' file.txt Note: $. is the line number, which equals total lines at EOF. ============================================================================ PART 10: HISTORICAL NOTE ------------------------ The butterfly appeared shortly after The Perl Journal published an interview with Chip Salzenberg explaining -n and -p internals. Abigail, legendary Perl hacker and regex wizard, saw the generated code structure and realized }{ would exploit it. The technique spread through the Perl community as a clever party trick that turned out to be genuinely useful. It's a reminder that Perl's quirks aren't bugs. They're features waiting to be discovered. ============================================================================ }{ / \ / \ | >< | \ / \ / \/ "Close one door, open another" - Perl proverb ============================================================================ japh.codes