Skip to content

-n and -p are a loop

Level: 101 · you have run a one-liner and want to know what it did

One line: perl -ne 'CODE' file is while (defined($_ = readline ARGV)) { CODE }, and -p adds a continue block that prints $_ — which is why next inside -p skips nothing, and why $. keeps counting from one file into the next.

Perl's switches look like modes. They are source code. -n wraps the text of -e in a loop that reads every line of every file named on the command line — standard input, if none is named — into $_. -p is the same loop with a continue block that prints $_ after each pass. B::Deparse, loaded as -MO=Deparse, compiles a program and prints it back as Perl without running it, so the loop is something to read rather than something to remember.

Measured

The script runs each one-liner over demo/orders.txt, and asks Deparse what -n and -p turned into:

Verified output of n_and_p_loop_sh.sh — regenerated by tools/run_examples.py, never hand-typed.

$ cat orders.txt
1001 alice Kraków 12.50
1002 bob Warsaw 7.25
1003 alice Gdańsk 30.00
1004 carol Warsaw 4.75

$ perl -ne 'print if /Warsaw/' orders.txt
1002 bob Warsaw 7.25
1004 carol Warsaw 4.75

$ perl -MO=Deparse -ne 'print if /Warsaw/' 2>/dev/null
LINE: while (defined($_ = readline ARGV)) {
    print $_ if /Warsaw/;
}

$ perl -pe 's/Warsaw/Warszawa/' orders.txt
1001 alice Kraków 12.50
1002 bob Warszawa 7.25
1003 alice Gdańsk 30.00
1004 carol Warszawa 4.75

$ perl -MO=Deparse -pe 's/Warsaw/Warszawa/' 2>/dev/null
LINE: while (defined($_ = readline ARGV)) {
    s/Warsaw/Warszawa/;
}
continue {
    die "-p destination: $!\n" unless print $_;
}

$ perl -pe 'next if /bob/' orders.txt
1001 alice Kraków 12.50
1002 bob Warsaw 7.25
1003 alice Gdańsk 30.00
1004 carol Warsaw 4.75

$ perl -ne 'next if /bob/; print' orders.txt
1001 alice Kraków 12.50
1003 alice Gdańsk 30.00
1004 carol Warsaw 4.75

$ perl -ne 'END { print "$. lines\n" }' orders.txt orders.txt
8 lines

$ perl -ne 'print "$ARGV $.\n" if eof; close ARGV if eof' orders.txt orders.txt
orders.txt 4
orders.txt 4

Reading what Deparse printed

  • LINE: — the loop has a label, so next LINE and last LINE work even from inside a loop of your own.
  • readline ARGVARGV is the filehandle behind <>. It opens each name in @ARGV in turn, reads standard input if there are none, and keeps the current file's name in $ARGV.
  • continue { die "-p destination: $!\n" unless print $_; } — under -p the print is not at the end of the loop body; it is in a continue block, which runs after every pass, including a pass that ended with next. So perl -pe 'next if /bob/' prints bob's line anyway. To leave a line out, use -n and print the lines you want, as the next command does. The die means a failed write stops the one-liner instead of losing lines quietly.

$. does not start again

$. is the number of the last line read from the last filehandle read — and ARGV is one filehandle that moves from file to file without being closed. Named twice, orders.txt ends at line 8. Closing ARGV at each file's eof resets it, which is the last command: each file reports its own 4.

If you are coming from another language

See also