I like to think of perl -n
as picking out specific bits of the input and perl -p
as map
for all lines of the input.
As you've observed, it's possible to get the effect of -p
with -n
, and we can emulate the other way around:
$ echo -e "1\n2\n3" | perl -pe '$_="" if $_ % 2 == 0'
1
3
Skipping lines with next
would seem more natural, but -p
wraps code in
LINE:
while (<>) {
... # your program goes here
} continue {
print or die "-p destination: $!\n";
}
By design, next
runs continue
blocks:
If there is a continue
BLOCK, it is always executed just before the conditional is about to be evaluated again. Thus it can be used to increment a loop variable, even when the loop has been continued via the next
statement.
The -l
switch has two handy effects:
- With
-n
and -p
, automatically chomp
each input record.
- Set
$\
so every print
implicitly adds a terminator.
For example, to grab the first 10 UDP ports mentioned in /etc/services
you might
perl -ane 'print $F[1] if $F[1] =~ /udp/' /etc/services | head
but oops:
7/udp9/udp11/udp13/udp17/udp19/udp37/udp39/udp42/ud...
Better:
$ perl -lane 'print $F[1] if $F[1] =~ /udp/' /etc/services | head
7/udp
9/udp
11/udp
13/udp
17/udp
19/udp
37/udp
39/udp
42/udp
53/udp
Remember that -n
and -p
can be in the shebang line too, so to save the above oneliner as a script:
#! /usr/bin/perl -lan
BEGIN {
@ARGV = ("/etc/services") unless @ARGV;
open STDOUT, "|-", "head" or die "$0: head failed";
}
print $F[1] if $F[1] =~ /udp/