Perl Part I · Module 15 · Command-Line Perl and Script Execution
-n and -p: The Implicit Loop
Nearly every useful one-liner does the same thing: read every line of the input and do something with each one. Writing that loop out costs more characters than the actual work, so Perl offers to write it for you. -n wraps your program in the loop and leaves the printing to you; -p wraps it in the same loop and prints each line when your code is done with it. Between them they cover most of what people use awk and sed for. The best thing about them is that you do not have to take anybody's word for what they do — Perl will print the exact loop it generated, and this chapter uses that rather than describing it.
Intermediate12 min readPerlPart ICommand Line
Part I · Module 15 · Chapter 15.7 · -n and -p: The Implicit Loop
1. The Loop You Do Not Write
use strict;
use warnings;
use File::Temp qw(tempdir);
use File::Spec;
my $dir = tempdir(CLEANUP => 1);
my $f = File::Spec->catfile($dir, "slack.txt");
open my $w, ">", $f or die "cannot write: $!\n";
print $w "U1/Q 0.44 setup\nU2/D -0.03 hold\nU3/CK 0.12 setup\n";
close $w;
my @demo = (
[ q{perl -ne 'print if /setup/'}, "-n: you decide what prints" ],
[ q{perl -pe 's/setup/SETUP/'}, "-p: every line prints, altered" ],
[ q{perl -ne '1'}, "-n with no print: nothing" ],
[ q{perl -ne 'print "$. $_"'}, "-n: \$. is the line number" ],
[ q{perl -pe '$_ = uc $_ if /hold/'}, "-p: change \$_ to change output" ],
);
for my $d (@demo) {
my ($cmd, $why) = @$d;
my $out = `cd '$dir' && $cmd slack.txt 2>&1`;
$out =~ s/\s+\z//;
$out = "(no output at all)" if $out eq "";
$out =~ s/\n/ | /g;
printf "%-34s %s\n -> %s\n", $cmd, "($why)", $out;
}perl -ne 'print if /setup/' (-n: you decide what prints)
-> U1/Q 0.44 setup | U3/CK 0.12 setup
perl -pe 's/setup/SETUP/' (-p: every line prints, altered)
-> U1/Q 0.44 SETUP | U2/D -0.03 hold | U3/CK 0.12 SETUP
perl -ne '1' (-n with no print: nothing)
-> (no output at all)
perl -ne 'print "$. $_"' (-n: $. is the line number)
-> 1 U1/Q 0.44 setup | 2 U2/D -0.03 hold | 3 U3/CK 0.12 setup
perl -pe '$_ = uc $_ if /hold/' (-p: change $_ to change output)
-> U1/Q 0.44 setup | U2/D -0.03 HOLD | U3/CK 0.12 setupThe difference in one sentence: -n gives you the loop, -p gives you the loop and a print.
-ne 'print if /setup/'printed two of three lines. The filter is yours.-pe 's/setup/SETUP/'printed all three, with the substitution applied. Nothing was filtered out, because-pprints every line whether you changed it or not.-ne '1'printed nothing. The loop ran three times and did nothing each time.
And $_ is the line. Everything from Module 4 about the default variable applies: /setup/ matches against $_, s/// substitutes in $_, print with no argument prints $_.
2. What They Actually Expand To
You do not have to believe any of that. -MO=Deparse prints the program Perl compiled, including the parts it added.
use strict;
use warnings;
for my $flag (qw(-n -p)) {
my $out = `perl -MO=Deparse $flag -e 's/old/new/' 2>&1`;
$out =~ s/^-e syntax OK\n//m;
$out =~ s/\s+\z//;
print "perl $flag -e 's/old/new/' expands to:\n";
print " $_\n" for split /\n/, $out;
print "\n";
}perl -n -e 's/old/new/' expands to:
LINE: while (defined($_ = readline ARGV)) {
s/old/new/;
}
perl -p -e 's/old/new/' expands to:
LINE: while (defined($_ = readline ARGV)) {
s/old/new/;
}
continue {
die "-p destination: $!\n" unless print $_;
}That is Perl's own account of itself, and three things in it are worth reading carefully.
while (defined($_ = readline ARGV)) is the loop. ARGV is the magic handle from Chapter 15.1 that reads every file named in @ARGV, or standard input when there are none. The defined test is what makes a final line of 0 with no newline work correctly.
LINE: is a real label. So last LINE and next LINE work inside your program text, which is how a one-liner stops early:
perl -ne 'last LINE if $. > 100; print' big.rpt # first 100 lines-p's print is in a continue block, not at the end of the body. That is not a detail — it changes behaviour:
perl -pe 'next if /^#/' file # does NOT drop comment linesnext skips the rest of the body and goes to continue, which prints. So the line is printed anyway. To actually drop lines you need -n and an explicit print, or you set $_ = "":
perl -ne 'print unless /^#/' file # correct: drops comments3. Choosing Between Them
A short rule: if you are filtering, use -n. If you are transforming, use -p.
| Job | Flag | Example |
|---|---|---|
| keep some lines | -n | perl -ne 'print if /ERROR/' |
| drop some lines | -n | perl -ne 'print unless /^#/' |
| change every line | -p | perl -pe 's/ps\b/e-12/g' |
| change some lines | -p | perl -pe 's/^/ / if /^sub /' |
| count or summarise | -n | perl -ne '$n++; END { print "$n\n" }' |
| number the lines | -p | perl -pe '$_ = "$. $_"' |
Two notes on that table.
-p transforms by changing $_. Either a substitution, which edits $_ in place, or an outright assignment as in the numbering row. If you print something yourself inside -p, it is printed in addition to the line, which is a common way to get doubled output.
-n with an END block is the counting idiom. The END runs once after the loop, exactly as Chapter 15.4 measured, so it is where a total belongs:
perl -lane '$sum += $F[1]; $n++; END { printf "%d rows, mean %.3f\n", $n, $sum/$n }' slack.txtBEGIN works the same way at the other end, which is where you set things up:
perl -ne 'BEGIN { $limit = 0.1 } print if (split)[1] < $limit' slack.txt4. The Variables That Come With The Loop
use strict;
use warnings;
use File::Temp qw(tempdir);
use File::Spec;
my $dir = tempdir(CLEANUP => 1);
for my $pair (["a.txt", "alpha\nbravo\n"], ["b.txt", "charlie\n"]) {
open my $w, ">", File::Spec->catfile($dir, $pair->[0]) or die "cannot write: $!\n";
print $w $pair->[1];
close $w;
}
my @demo = (
[ q{perl -ne 'print "$ARGV:$.: $_"'}, "\$ARGV is the file, \$. the line number" ],
[ q{perl -ne 'print "$.\n" if eof'}, "eof is true on the last line of EACH file" ],
[ q{perl -ne 'print "$.\n" if eof()'}, "eof() with parens: the very last line of ALL" ],
[ q{perl -ne 'print "$ARGV:$.\n"'}, "no close: \$. runs on across files" ],
[ q{perl -ne 'print "$ARGV:$.\n"; close ARGV if eof'}, "close AFTER print: per-file numbers" ],
[ q{perl -ne 'close ARGV if eof; print "$ARGV:$.\n"'}, "close BEFORE print: the trap" ],
);
for my $d (@demo) {
my ($cmd, $why) = @$d;
my $out = `cd '$dir' && $cmd a.txt b.txt 2>&1`;
$out =~ s/\s+\z//;
$out =~ s/\n/ | /g;
printf "%s\n %s\n -> %s\n\n", $cmd, "($why)", $out;
}perl -ne 'print "$ARGV:$.: $_"'
($ARGV is the file, $. the line number)
-> a.txt:1: alpha | a.txt:2: bravo | b.txt:3: charlie
perl -ne 'print "$.\n" if eof'
(eof is true on the last line of EACH file)
-> 2 | 3
perl -ne 'print "$.\n" if eof()'
(eof() with parens: the very last line of ALL)
-> 3
perl -ne 'print "$ARGV:$.\n"'
(no close: $. runs on across files)
-> a.txt:1 | a.txt:2 | b.txt:3
perl -ne 'print "$ARGV:$.\n"; close ARGV if eof'
(close AFTER print: per-file numbers)
-> a.txt:1 | a.txt:2 | b.txt:1
perl -ne 'close ARGV if eof; print "$ARGV:$.\n"'
(close BEFORE print: the trap)
-> a.txt:1 | a.txt:0 | b.txt:0Four variables and one function, measured together:
$_ is the current line, including its newline unless you used -l.
$. is the line number, and it does not reset between files by default — the second file continues the count. That surprises people reading several reports at once.
$ARGV is the current filename, which is what you put in a message about a bad line.
eof without parentheses is true at the end of each file; eof() with parentheses is true only at the end of the last one. That is a genuine difference and the measured rows show it: one prints a number per file, the other prints one number.
close ARGV if eof; resets $. for the next file — but the order matters, and getting it wrong is silent. The last three rows measure all three possibilities:
- No
closegives1, 2, 3— the count runs straight on into the second file. closeafter the print gives1, 2, 1— correct per-file numbering.closebefore the print gives1, 0, 0— because on the last line of each file the reset happens first, so the line that is printed reports zero.
The middle form is the idiom. The third is the one to watch for: it produces plausible small numbers and no error.
5. What To Remember
-nwraps your code in a loop over every input line. You decide what to print.-pis the same loop plus a print, so every line comes out whether you changed it or not.- Measured:
-ne '1'printed nothing — the loop ran and did nothing. $_is the line, so/match/,s///and bareprintall work on it.-MO=Deparseprints the loop Perl generated, so none of this has to be taken on trust.- The loop is
while (defined($_ = readline ARGV)), reading every file in@ARGVor standard input. LINE:is a real label —last LINE if $. > 100stops early.-pprints from acontinueblock, sonextstill prints the line. Use-nwith an explicit print to drop lines.-palso checks that its print succeeded and dies if not.- Filter with
-n, transform with-p. -ptransforms by changing$_; printing inside-padds output rather than replacing it.-nwithEND { }is the counting idiom, andBEGIN { }is where setup goes.$.is the line number and does NOT reset between files — measured1, 2, 3across two files.close ARGV if eof;resets it, but only if it comes AFTER the print — measured1, 2, 1after, against1, 0, 0before.eofis per-file;eof()is the very end of all input. Measured, and the parentheses are the whole difference.
Chapter 15.8 adds the flag that makes these loops genuinely competitive with awk. -a splits each line into fields for you, and -F chooses what counts as a separator.
Where this fits
Part of the Perl curriculum.
