Record Separator
The $/ variable. Input record separator. Most people never touch it.By default it's a newline. That's why <> reads one line at a time. But change $/ and you change how Perl sees the world.
One variable. Four completely different behaviors.$/= undef; # Slurp entire file $/= ""; # Paragraph mode $/= \1024; # Read 1024 bytes at a time $/= "END"; # Read until "END"
Part 1: SLURP MODE
That's it. The whole file in one scalar. No loop needed.$/ = undef; my $entire_file = <>;
In a one-liner:
The -0777 sets $/ to undef. The 777 is octal for "nothing" - there's no character 0777, so Perl interprets it as "no separator at all."perl -0777 -ne 'print length' file.txt
When would you use this? Multi-line regex matches:
Without slurp mode, that regex can't cross line boundaries.perl -0777 -pe 's/START.*?END/REPLACED/gs' file.txt
Part 2: PARAGRAPH MODE
Empty string. Now Perl reads paragraph by paragraph - chunks of text separated by blank lines.$/ = "";
The -00 enables paragraph mode. Each $ is a full paragraph. If any line in that paragraph contains "error", print the whole thing.perl -00 -ne 'print if /error/i' logfile.txt
Context around your matches. For free.
.--. |o_o | |:_/ | // \ \ (| | ) /'\_ _/`\ \___)=(___/
Part 3: FIXED-WIDTH RECORDS
Here's where it gets weird. Set $/ to a reference to a number:Now <> reads exactly 1024 bytes at a time. Not lines. Bytes.$/ = \1024;
Reads 16 bytes per iteration. Perfect for binary files with fixed record sizes.perl -e '$/ = \16; print "<$_>\n" while <>' binary.dat
Database dumps. Network packet captures. Old mainframe files with 80-character records. This handles all of them.
Part 4: CUSTOM DELIMITERS
Now Perl reads until it hits the literal string "END".$/ = "END";
Ghetto XML parsing. Each $ contains everything up to and including the </record> tag.perl -e '$/ = "</record>"; print "---\n$_" while <>' data.xml
Not proper parsing. But when you need something quick and dirty on a server with no XML libraries? This works.
Part 5: THE SWITCHES
Command line shortcuts:The -0 without a number means null byte - pairs perfectly with find -print0 and xargs -0:SWITCH EQUIVALENT MEANING ------ ----------------- --------------------------- -0 $/ = "\0" Null-separated (find -print0) -00 $/ = "" Paragraph mode -0777 $/ = undef Slurp mode -0x $/ = chr(x) Octal character code
Null-separated filenames. No issues with spaces or newlines in paths.find . -name "*.log" -print0 | perl -0 -ne 'print if -M $_ > 7'
Part 6: BINARY CHUNKING
Processing a large binary file in chunks:Progress indicator for large file processing. Each read is exactly 4096 bytes (except possibly the last one).perl -e ' $/ = \4096; my $total = 0; while (<>) { $total += length; print STDERR "Read $total bytes\r"; } print STDERR "\nDone: $total bytes\n"; ' hugefile.bin
Part 7: RECORD-ORIENTED DATA
Old-school fixed-width data:Twenty bytes per record. Unpack pulls out the fields. No parsing needed because the structure is fixed.# Data file: 10-char name, 3-char age, 7-char salary # JohnSmith 034 0085000 # JaneDoe 028 0092000 perl -e ' $/ = \20; while (<>) { my ($name, $age, $salary) = unpack("A10 A3 A7", $_); print "$name: $age years, \$$salary\n"; } ' payroll.dat
Part 8: MULTIPLE FILES
When processing multiple files, $/ persists:Each file is slurped separately. $ARGV tells you which file.perl -0777 -ne 'print "$ARGV: " . length . " bytes\n"' *.txt
But watch out - $. (line number) doesn't reset between files unless you explicitly close ARGV:
The butterfly operator strikes again.perl -00 -ne 'print "$ARGV:$.: $_" }{ close ARGV' *.txt
Part 9: COMBINING WITH OUTPUT
There's also $\ - the output record separator:Every print automatically appends "---" between paragraphs.$/ = ""; # Read paragraphs $\ = "\n---\n"; # Print separator after each output while (<>) { print if /important/; }
In one-liner form:
The -l sets $\ to match $/ (sort of). Output gets auto-newlined.perl -00 -l -ne 'print if /error/' logfile.txt
Part 10: THE MAGIC TABLE
Quick reference:$/ READS ------------ ------------------------------------ "\n" One line (default) undef Entire file "" Paragraph (text between blank lines) \N Exactly N bytes "STRING" Until STRING appears "\0" Until null byte SWITCH EQUIVALENT ------ ---------- -0 $/ = "\0" -00 $/ = "" -0777 $/ = undef -0NNN $/ = chr(oct("NNN"))
Part 11: REAL WORLD
Extracting functions from C code:Finding the longest line in a file:perl -0777 -ne 'print "$1\n" while /^(\w+ \w+\([^)]*\))\s*{/gm' *.c
Processing CSV with embedded newlines (fields quoted):perl -0777 -ne 'print length($1) while /(.+)/g' file.txt | sort -n | tail -1
The record separator is one of Perl's quiet superpowers. Most languages give you lines. Perl gives you whatever you want.perl -0777 -ne ' while (/"([^"]*)"/) { my $clean = $1; $clean =~ s/\n/\\n/g; s/"[^"]*"/"$clean"/; } print ' data.csv
$/ / \ / \ | ?? | \ / \ / \/ Define your own reality
Created By: Wildcard Wizard. Copyright 2026