Skip to content

-l, -a and -F take a line apart

Level: 101 · you want awk's fields without awk

One line: -a splits each line into @F with split(' ', $_), -F swaps in a pattern of your own — and because the line still ends in \n, the last field keeps it unless -l chomps it first, which is why a -F: one-liner prints a stray line break and -F: -l does not.

Measured

The script runs over three files in demo/: space-separated orders, a colon-separated accounts file, and a tab-separated one.

Verified output of fields_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 -ane 'print $F[2]' orders.txt
KrakówWarsawGdańskWarsaw
$ perl -lane 'print $F[2]' orders.txt
Kraków
Warsaw
Gdańsk
Warsaw

$ perl -MO=Deparse -lane 'print $F[2]' 2>/dev/null
BEGIN { $/ = "\n"; $\ = "\n"; }
LINE: while (defined($_ = readline ARGV)) {
    chomp $_;
    our @F = split(' ', $_, 0);
    print $F[2];
}

$ perl -lane '$total += $F[3]; END { print $total }' orders.txt
54.5

$ perl -lane 'print "$F[1] $F[-1]" if $F[3] > 10' orders.txt
alice 12.50
alice 30.00

$ cat accounts.txt
root:x:0:0:System Administrator:/var/root:/bin/sh
ola:x:501:20:Ola Nowak:/home/ola:/bin/zsh

$ perl -F: -ane 'print "[$F[-1]]"' accounts.txt
[/bin/sh
][/bin/zsh
]
$ perl -F: -lane 'print "[$F[-1]]"' accounts.txt
[/bin/sh]
[/bin/zsh]

$ perl -F: -le 'print $F[0]' accounts.txt
root
ola

$ cat cities.tsv
id	city	note
1	New York	big apple
2	Kraków	old town

$ perl -lane 'print $F[1]' cities.tsv
city
New
Kraków

$ perl -F'\t' -lane 'print $F[1]' cities.tsv
city
New York
Kraków

What each switch added

Deparse shows three additions to the -n loop:

  • -l became BEGIN { $/ = "\n"; $\ = "\n"; } and a chomp $_; at the top of the loop. The chomp removes the newline from each line; $\ is the output record separator, printed after every print. Without -l, print $F[2] runs the four cities together into one line.
  • -a became our @F = split(' ', $_, 0);. The pattern ' ' — a string holding one space — is split's special case: split on runs of whitespace and ignore whitespace at the start. The split lesson measures the difference from / /.
  • -F: became split(/:/, $_, 0). Since perl 5.20, -F implies -a and -n, so perl -F: -le 'print $F[0]' is a complete one-liner.

The newline on the last field

With -a alone the newline is harmless: ' ' counts \n as whitespace and drops it. -F: does not — the last field of each accounts line is /bin/sh plus a newline, and the brackets in the output show the line break landing inside them. -l removes the newline before the split, so every field is clean.

A space is not a tab

In cities.tsv the city is New York, with a space inside a tab-separated field. -a splits on any whitespace, so $F[1] is New. -F'\t' splits on tabs only and gives New York. The quotes matter: they pass the backslash through the shell to perl.

Arithmetic on fields

Every field is a string. $total += $F[3] converts each one to a number, and the END block runs once the loop has read the last line. The total prints as 54.5, not 54.50: a number keeps no formatting of its own, which Numbers from text takes further.

If you are coming from another language

See also