Records are not always lines¶
Level: 201 · your data is paragraphs, one blob, fixed-size blocks, or file names from find -print0
One line: <> reads up to $/, the input record separator, which is "\n" only by default — -00 sets it to "" for paragraphs, -0777 to undef for the whole file as one record, $/ = \8 reads eight bytes at a time, and bare -0 splits on NUL — and $. counts records, not lines.
Measured¶
The script reads demo/notes.txt — three paragraphs, with two blank lines before the last — and demo/fixed.dat, which has no newlines at all:
Verified output of records_sh.sh — regenerated by tools/run_examples.py, never hand-typed.
$ cat notes.txt
Kraków
Main Square
Cloth Hall
Gdańsk
Long Market
Warsaw
Old Town
$ perl -ne 'END { print "$. records\n" }' notes.txt
10 records
$ perl -00 -ne 'END { print "$. records\n" }' notes.txt
3 records
$ perl -00 -ne 'print "record $.: ", (split /\n/)[0], "\n"' notes.txt
record 1: Kraków
record 2: Gdańsk
record 3: Warsaw
$ perl -MO=Deparse -00 -ne 'print' 2>/dev/null
BEGIN { $/ = ""; $\ = undef; }
LINE: while (defined($_ = readline ARGV)) {
print $_;
}
$ perl -0777 -ne 'print length, " bytes, ", tr/\n//, " newlines, one record\n"' notes.txt
70 bytes, 10 newlines, one record
$ perl -MO=Deparse -0777 -ne 'print' 2>/dev/null
BEGIN { $/ = undef; $\ = undef; }
LINE: while (defined($_ = readline ARGV)) {
print $_;
}
$ perl -0777 -pe 's/\n{3,}/\n\n/g' notes.txt
Kraków
Main Square
Cloth Hall
Gdańsk
Long Market
Warsaw
Old Town
$ cat fixed.dat
WAW00123KRK00045GDN00678
$ perl -ne 'BEGIN { $/ = \8 } print "[$_]\n"' fixed.dat
[WAW00123]
[KRK00045]
[GDN00678]
$ printf 'first\0second\0' | perl -0 -ne 'chomp; print "[$_]\n"'
[first]
[second]
Four separators¶
- Lines — the default,
$/ = "\n".notes.txtis ten records, and$.says 10. - Paragraphs —
-00, which Deparse shows as$/ = "". The empty string is a special value: one or more blank lines end a record, so the double blank line beforeWarsawstill makes three records, not four.(split /\n/)[0]then takes each paragraph's first line. - The whole file —
-0777, which Deparse shows as$/ = undef. There is no character 0777 (it is past0377, the largest octal byte), so perl reads without a separator. With the file in one record, a substitution can see across line breaks:s/\n{3,}/\n\n/gturns the double blank line into a single one, which no line-at-a-time loop can do. - Fixed-size blocks —
$/ = \8, a reference to a number, reads records of that size.fixed.datis three eight-byte records with no separator between them. There is no switch for this one, so the one-liner sets$/in aBEGINblock.
And -0 with no digits sets $/ to the NUL character, the separator find -print0 and xargs -0 use for file names that may contain spaces or newlines. chomp removes whatever $/ is, so it removes the NUL.
$. counts records¶
Whatever $/ is, $. goes up by one per read. After -00 it is a paragraph number; after -0777 it is 1. A message that says "error on line $." is wrong for every separator but the default.
If you are coming from another language¶
- awk.
RS=""is awk's paragraph mode, and it is where-00comes from. - Python. A file object iterates over lines only, and splitting on anything else is yours to write. Python's What ends a line ↗ compares its own rules with
$/and Perl's\R. - The encodings library. The trailing newline ↗ — what tools do with a last line that has no
\n.
See also¶
chompleaves the\r—chompremoves exactly$/, and a Windows file's lines end in something else-l,-aand-Ftake a line apart —-lsets$\from$/- perlvar,
$/↗