perl.gg / one-liners

<!-- category: one-liners -->

The -0777 Slurp Mode Switch

2026-04-05

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:

perl -0777 -ne 'print if /start.*?end/s' file.txt
That -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.
SWITCH $/ 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)
The -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:
perl -0777 -ne 'print "Found it\n" if /BEGIN.*?END/s' config.txt
The /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:

perl -0777 -ne 'print $1 while /(BEGIN\n.*?\nEND)/sg' data.txt
Count how many times a multi-line pattern appears:
perl -0777 -ne '$n++ while /function\s*\w+\s*\{/g; print "$n\n"' code.c
All of these would be impossible (or extremely painful) with line-by-line reading.

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:
perl -0777 -pe 's/old_function\(.*?\)/new_function()/sg' code.pl
That replaces every call to old_function(...) with new_function(), even if the arguments span multiple lines.

Remove C-style block comments from a file:

perl -0777 -pe 's~/\*.*?\*/~~sg' source.c
Collapse consecutive blank lines into one:
perl -0777 -pe 's/\n{3,}/\n\n/g' document.txt
These one-liners modify the output. They do not change the original file. To edit in place, add -i:
perl -0777 -i -pe 's/\n{3,}/\n\n/g' document.txt
Now the file is modified on disk. Use -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:
#!/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"; } }
The 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.
perl -00 -ne 'print if /keyword/' document.txt
This prints every paragraph that contains "keyword". A paragraph is defined as text separated by one or more blank lines.

Paragraph mode is great for structured documents:

perl -00 -ne 'print if /^Name:/m' contacts.txt
This prints every contact block (separated by blank lines) that has a "Name:" field.
SWITCH MODE SEPARATOR ------ ---- --------- -0777 slurp (none, read entire file) -00 paragraph blank line(s) (none) line newline -0 null \0 byte
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 for 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:
perl -0777 -ne 'print "$ARGV: " . (() = /TODO/g) . " TODOs\n"' *.pl
This counts TODO occurrences per file. $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:

perl -0777 -i -pe 's~^# Copyright.*?\n\n~# Copyright 2026 Perl.gg\n\n~s' lib/*.pm
Each file is read in full, the substitution runs, and the file is rewritten. One command, many files, multi-line patterns.

Part 7: COMBINING WITH OTHER SWITCHES

The -0777 switch plays well with others:
# -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
The -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:
perl -0777 -ne 'print "$1\n" while /^sub\s+(\w+)/mg' lib/Module.pm
Remove all HTML tags from a file:
perl -0777 -pe 's~<[^>]+>~~sg' page.html
Extract the contents of a specific XML element:
perl -0777 -ne 'print $1 if m~<description>(.*?)</description>~s' data.xml
Reformat a multi-line SQL query into one line:
perl -0777 -pe 's/\s+/ /g; s/^\s+//; s/\s+$/\n/' query.sql
Find files containing a multi-line pattern (like grep, but across lines):
perl -0777 -nle 'print $ARGV if /class\s+\w+\s*\{.*?constructor/s' *.js
Swap two blocks of text:
perl -0777 -pe 's~(BLOCK_A.*?END_A)(.*?)(BLOCK_B.*?END_B)~$3$2$1~s' doc.txt
Every one of these would require a multi-line script or an awkward pipeline without slurp mode. With -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.
FILE 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
For large files, process line by line. That is what line mode is for. Only use -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:

my $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); } }
Not as elegant as -0777, but it works on files of any size.

Part 10: THE 777 EXPLANATION

"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." // \ \ (| | ) /'\_ _/`\ \___)=(___/
The -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