<!-- category: one-liners -->
The -0777 Slurp Mode Switch
You need to match a pattern that spans multiple lines. A regex that starts on line 12 and ends on line 15. The default line-by-line reading will never see it because it processes one line at a time.The fix is three characters on the command line:
Thatperl -0777 -ne 'print if /start.*?end/s' file.txt
-0777 tells Perl to read the entire file as one giant
string instead of splitting it into lines. Your multi-line regex
now sees everything at once.
Why 777? Because the -0 switch sets the input record separator
using an octal value, and octal 0777 is decimal 511. No byte can
have a value of 511. Since the record separator can never be
found, Perl reads until EOF. The entire file becomes one "line."
It is the kind of numeric hack that only makes sense in Perl, and it works beautifully.
Part 1: WHAT -0 DOES
The-0 command-line switch sets $/, the input record
separator. Normally $/ is "\n", which is why Perl reads
one line at a time. But -0 lets you change it from the
command line.
TheSWITCH $/ EFFECT ------ ---- ------ (none) "\n" read one line at a time (default) -0 "\0" split on null bytes -0NNN chr(oct(NNN)) split on octal byte value NNN -0777 (special) slurp entire file, no splitting -00 "" paragraph mode (split on blank lines)
-0 with no number sets the separator to null (\0), which
is useful for null-delimited input like find -print0.
But -0 followed by an octal number sets the separator to
that character. -012 sets it to newline (octal 12 = decimal
10 = \n). -072 sets it to colon (octal 72 = decimal 58).
And -0777? Octal 777 is 511. The input record separator is a
single-byte concept. No byte value is 511. So Perl can never
find the separator, which means it reads everything until end of
file. Slurp mode.
Part 2: YOUR FIRST SLURP
Find a multi-line pattern in a file:Theperl -0777 -ne 'print "Found it\n" if /BEGIN.*?END/s' config.txt
/s modifier makes . match newlines. Combined with
-0777, you are running one big regex against the entire file
contents. Lines are irrelevant. The file is just one string.
Extract everything between two markers:
Count how many times a multi-line pattern appears:perl -0777 -ne 'print $1 while /(BEGIN\n.*?\nEND)/sg' data.txt
All of these would be impossible (or extremely painful) with line-by-line reading.perl -0777 -ne '$n++ while /function\s*\w+\s*\{/g; print "$n\n"' code.c
Part 3: MULTI-LINE FIND AND REPLACE
The real power shows up with-p and substitution. The -p
flag prints $_ after every "line" is processed. With -0777,
there is only one "line" (the whole file), so it prints the
whole file after your substitution:
That replaces every call toperl -0777 -pe 's/old_function\(.*?\)/new_function()/sg' code.pl
old_function(...) with
new_function(), even if the arguments span multiple lines.
Remove C-style block comments from a file:
Collapse consecutive blank lines into one:perl -0777 -pe 's~/\*.*?\*/~~sg' source.c
These one-liners modify the output. They do not change the original file. To edit in place, addperl -0777 -pe 's/\n{3,}/\n\n/g' document.txt
-i:
Now the file is modified on disk. Useperl -0777 -i -pe 's/\n{3,}/\n\n/g' document.txt
-i.bak to keep a
backup:
perl -0777 -i.bak -pe 's/\n{3,}/\n\n/g' document.txt
Part 4: COMPARISON TO $/ = UNDEF
Inside a Perl script, the equivalent of-0777 is setting
$/ to undef:
The#!/usr/bin/env perl use strict; use warnings; use feature 'say'; { local $/; # undef - slurp mode open my $fh, '<', 'data.txt' or die $!; my $contents = <$fh>; close $fh; # now $contents has the entire file if ($contents =~ m~BEGIN(.+?)END~s) { say "Found: $1"; } }
local $/ undefines the record separator within the block.
The <$fh> read returns everything until EOF. When the block
ends, $/ is restored to "\n".
Both approaches do the same thing. The -0777 flag is for
command-line one-liners. The local $/ idiom is for scripts.
Choose based on context.
Part 5: PARAGRAPH MODE WITH -00
The sibling of-0777 is -00, which sets $/ to "". This
is paragraph mode: Perl splits input on blank lines instead
of newlines.
This prints every paragraph that contains "keyword". A paragraph is defined as text separated by one or more blank lines.perl -00 -ne 'print if /keyword/' document.txt
Paragraph mode is great for structured documents:
This prints every contact block (separated by blank lines) that has a "Name:" field.perl -00 -ne 'print if /^Name:/m' contacts.txt
These four modes cover 95% of text processing needs. Line mode for logs and CSVs. Paragraph mode for structured text. Slurp mode for multi-line patterns. Null mode forSWITCH MODE SEPARATOR ------ ---- --------- -0777 slurp (none, read entire file) -00 paragraph blank line(s) (none) line newline -0 null \0 byte
find -print0
pipelines.
Part 6: PROCESSING MULTIPLE FILES
With-0777, each file is slurped separately. If you pass
multiple files, the code runs once per file:
This counts TODO occurrences per file.perl -0777 -ne 'print "$ARGV: " . (() = /TODO/g) . " TODOs\n"' *.pl
$ARGV contains the
current filename. The () = /TODO/g trick forces list context
to count matches.
Replace a header block across all files in a directory:
Each file is read in full, the substitution runs, and the file is rewritten. One command, many files, multi-line patterns.perl -0777 -i -pe 's~^# Copyright.*?\n\n~# Copyright 2026 Perl.gg\n\n~s' lib/*.pm
Part 7: COMBINING WITH OTHER SWITCHES
The-0777 switch plays well with others:
The# -l adds output newlines (useful with -0777 since there are none) perl -0777 -lne 'print length' file.txt # -a and -F still work (split on custom separator) perl -0777 -F'/\n\n/' -ane 'print scalar @F, " paragraphs\n"' doc.txt # -w enables warnings perl -0777 -wne 'print if /pattern/s' file.txt
-l switch is particularly useful. Without it, -0777 -ne
does not add newlines to your output since the record separator
is gone. Adding -l restores sane line endings.
Part 8: REAL-WORLD RECIPES
Extract all function definitions from a Perl file:Remove all HTML tags from a file:perl -0777 -ne 'print "$1\n" while /^sub\s+(\w+)/mg' lib/Module.pm
Extract the contents of a specific XML element:perl -0777 -pe 's~<[^>]+>~~sg' page.html
Reformat a multi-line SQL query into one line:perl -0777 -ne 'print $1 if m~<description>(.*?)</description>~s' data.xml
Find files containing a multi-line pattern (like grep, but across lines):perl -0777 -pe 's/\s+/ /g; s/^\s+//; s/\s+$/\n/' query.sql
Swap two blocks of text:perl -0777 -nle 'print $ARGV if /class\s+\w+\s*\{.*?constructor/s' *.js
Every one of these would require a multi-line script or an awkward pipeline without slurp mode. Withperl -0777 -pe 's~(BLOCK_A.*?END_A)(.*?)(BLOCK_B.*?END_B)~$3$2$1~s' doc.txt
-0777, they are
one-liners.
Part 9: MEMORY CONSIDERATIONS
Slurp mode reads the entire file into memory. For a 100 KB config file, this is nothing. For a 10 GB log file, this will eat all your RAM and crash.For large files, process line by line. That is what line mode is for. Only useFILE SIZE -0777 FEASIBLE? --------- ---------------- < 1 MB always fine 1-100 MB fine on modern machines 100 MB - 1 GB be careful > 1 GB don't do it
-0777 when you genuinely need the whole file
as one string.
If you need multi-line matching on large files but cannot slurp, consider reading in chunks or using a state machine approach:
Not as elegant asmy $buffer = ''; while (my $line = <$fh>) { $buffer .= $line; while ($buffer =~ s~^(.*?PATTERN.*?\n)~~s) { process($1); } # keep buffer from growing unbounded if (length($buffer) > 1_000_000) { # process or discard the oldest part $buffer = substr($buffer, -500_000); } }
-0777, but it works on files of any size.
Part 10: THE 777 EXPLANATION
The"Why 777?" OCTAL DECIMAL BYTE? ----- ------- ----- 0 0 yes (null) 12 10 yes (newline) 72 58 yes (colon) 777 511 NO (too big!) A byte is 0-255. 511 is not a byte. The separator can never match. Perl reads forever. Slurp. .--. |o_o | "Read it all. |:_/ | Ask questions never." // \ \ (| | ) /'\_ _/`\ \___)=(___/
-0777 switch is the duct tape of Perl one-liners. It turns
multi-line text processing from a chore into a one-liner. It
works because of a beautifully simple idea: set the record
separator to something that can never exist, and Perl reads
everything as one record.
No special API. No "read all" function. No modes or flags on the file open call. Just an impossible separator value that falls naturally out of how Perl already works.
That is the kind of elegance you find when a language has been used by sysadmins and text hackers for 40 years. Someone needed slurp mode. Someone realized 0777 was past the byte range. And it became the standard idiom, passed down through a thousand one-liner collections like campfire knowledge.
Three characters. -0777. Read the whole file. Get on with
your life.
perl.gg