-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:
-lbecameBEGIN { $/ = "\n"; $\ = "\n"; }and achomp $_;at the top of the loop. Thechompremoves the newline from each line;$\is the output record separator, printed after everyprint. Without-l,print $F[2]runs the four cities together into one line.-abecameour @F = split(' ', $_, 0);. The pattern' '— a string holding one space — issplit's special case: split on runs of whitespace and ignore whitespace at the start. Thesplitlesson measures the difference from/ /.-F:becamesplit(/:/, $_, 0). Since perl 5.20,-Fimplies-aand-n, soperl -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¶
- awk.
-Fis awk's-F, but@Fcounts from zero: awk's$1is$F[0]. The encodings library measurescut↗ andawk↗ on the same kind of file. - Python.
line.split()with no argument is-a's' ';line.split(':')keeps the newline on the last field exactly as-F:does. See What ends a line ↗ andstripis a set, not a prefix ↗. - Rust. Walking a
String↗ —split(' ')is mechanical andsplit_whitespaceis-a.
See also¶
-nand-pare a loop — the loop these lines are added tosplithas sharp edges — the function-acalls, and its rules for empty fields- Records are not always lines — when
$/, which-lalso sets, is not"\n"