Skip to content
VLSI Mentor

Perl Part I · Module 15 · Command-Line Perl and Script Execution

-a and -F: Autosplit Mode

Report files are columns. A timing report has a pin, a slack and a check type; a netlist line has a cell and an instance; a log has a timestamp, a severity and a message. Almost every question you want to ask is about one column, and splitting the line to get at it is the step you would otherwise write every time. The -a flag does it for you, putting the fields in an array called @F. This chapter covers that, the -F flag that handles commas and colons, and a measured difference between two separators that look identical and are not — the one that silently shifts every column by one on an indented line.

Intermediate12 min readPerlPart ICommand Line

Part I · Module 15 · Chapter 15.8 · -a and -F: Autosplit Mode

1. -a Puts the Fields in @F

Azvya Education Pvt. Ltd.VLSI Mentor
autosplit-basics.pl
use strict;
use warnings;
use File::Temp qw(tempdir);
use File::Spec;

my $dir = tempdir(CLEANUP => 1);
open my $w, ">", File::Spec->catfile($dir, "slack.txt") 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 -lane 'print $F[0]'},                   "first field — index ZERO" ],
    [ q{perl -lane 'print $F[1]'},                   "second field" ],
    [ q{perl -lane 'print scalar @F'},               "how many fields" ],
    [ q{perl -lane 'print "$F[0] $F[1]" if $F[1] < 0'}, "a filter on a column" ],
    [ q{perl -lane 'print $F[-1]'},                  "last field, Perl-style" ],
    [ q{perl -lane 'print "@F[0,2]"'},               "an array slice of fields" ],
);

for my $d (@demo) {
    my ($cmd, $why) = @$d;
    my $out = `cd '$dir' && $cmd slack.txt 2>&1`;
    $out =~ s/\s+\z//;
    $out =~ s/\n/ | /g;
    printf "%-50s -> %-28s (%s)\n", $cmd, $out, $why;
}
Azvya Education Pvt. Ltd.VLSI Mentor
Captured output
perl -lane 'print $F[0]'                           -> U1/Q | U2/D | U3/CK          (first field — index ZERO)
perl -lane 'print $F[1]'                           -> 0.44 | -0.03 | 0.12          (second field)
perl -lane 'print scalar @F'                       -> 3 | 3 | 3                    (how many fields)
perl -lane 'print "$F[0] $F[1]" if $F[1] < 0'      -> U2/D -0.03                   (a filter on a column)
perl -lane 'print $F[-1]'                          -> setup | hold | setup         (last field, Perl-style)
perl -lane 'print "@F[0,2]"'                       -> U1/Q setup | U2/D hold | U3/CK setup (an array slice of fields)

@F is zero-based. $F[0] is the first field.

That sentence is the whole reason this section exists. In awk, $1 is the first field and $0 is the whole line. In Perl, $F[0] is the first field and $_ is the whole line. Everyone coming from awk writes $F[1] for the first column at least once, and gets the second — which on a timing report means reading the slack where they meant the pin, and getting an answer rather than an error.

The rest is ordinary Perl array behaviour from Module 5: $F[-1] is the last field, which awk needs $NF for, and @F[0,2] is a slice of two columns.

2. -F Chooses the Separator

Without -F, fields are separated by whitespace. For anything else, say so.

Azvya Education Pvt. Ltd.VLSI Mentor
separators.pl
use strict;
use warnings;
use File::Temp qw(tempdir);
use File::Spec;

my $dir = tempdir(CLEANUP => 1);
my %file = (
    "c.csv"  => "U1/Q,0.44,setup\nU2/D,-0.03,hold\n",
    "p.txt"  => "U1/Q:0.44:setup\nU2/D:-0.03:hold\n",
    "m.txt"  => "U1/Q::0.44  setup\n",
    "t.tsv"  => "U1/Q\t0.44\tsetup\n",
);
for my $n (sort keys %file) {
    open my $w, ">", File::Spec->catfile($dir, $n) or die "cannot write: $!\n";
    print $w $file{$n};
    close $w;
}

my @demo = (
    [ q{perl -F, -lane 'print "$F[0]|$F[1]"' c.csv},            "comma" ],
    [ q{perl -F: -lane 'print "$F[0]|$F[2]"' p.txt},            "colon" ],
    [ q{perl -F'\t' -lane 'print "$F[0]|$F[1]"' t.tsv},         "a tab, escaped" ],
    [ q{perl -F'[:\s]+' -lane 'print scalar(@F), ":", join("|",@F)' m.txt}, "a regex: runs of colon or space" ],
    [ q{perl -F, -lane 'print join("|", reverse @F)' c.csv},    "columns reordered" ],
);

for my $d (@demo) {
    my ($cmd, $why) = @$d;
    my $out = `cd '$dir' && $cmd 2>&1`;
    $out =~ s/\s+\z//;
    $out =~ s/\n/ ;; /g;
    printf "%s\n   -> %-26s (%s)\n", $cmd, $out, $why;
}
Azvya Education Pvt. Ltd.VLSI Mentor
Captured output
perl -F, -lane 'print "$F[0]|$F[1]"' c.csv
   -> U1/Q|0.44 ;; U2/D|-0.03    (comma)
perl -F: -lane 'print "$F[0]|$F[2]"' p.txt
   -> U1/Q|setup ;; U2/D|hold    (colon)
perl -F'\t' -lane 'print "$F[0]|$F[1]"' t.tsv
   -> U1/Q|0.44                  (a tab, escaped)
perl -F'[:\s]+' -lane 'print scalar(@F), ":", join("|",@F)' m.txt
   -> 3:U1/Q|0.44|setup          (a regex: runs of colon or space)
perl -F, -lane 'print join("|", reverse @F)' c.csv
   -> setup|0.44|U1/Q ;; hold|-0.03|U2/D (columns reordered)

Three points about -F:

A single character needs no quotes from Perl's side, but the shell may need them. -F, is fine; -F'\t' and -F'|' need the quotes because the shell would otherwise mangle them.

-F takes a pattern, not just a character. -F'[:\s]+' splits on any run of colons and whitespace — the full regular expression language from Module 11 is available, which is where this beats awk -F for messy input.

A literal | must be escaped, since it is alternation in a pattern: -F'\|'.

3. Coming From awk

The translation is mostly mechanical:

awkPerl one-liner
$0$_
$1$F[0]
$NF$F[-1]
NFscalar @F
NR$.
FILENAME$ARGV
-F,-F,
BEGIN { }BEGIN { }
END { }END { }
awk '{print $1}'perl -lane 'print $F[0]'
awk '$2 < 0 {print}'perl -lane 'print if $F[1] < 0'
awk '{s+=$2} END{print s}'perl -lane '$s+=$F[1]; END{print $s}'

Two differences that are not just naming:

Perl's -F takes a regex; awk's takes a string (with some awks accepting an ERE). For input separated by "a colon and some spaces, maybe", Perl handles it directly.

Perl has the rest of Perl. Hashes, sorting with a custom comparator, modules. The moment a summary needs grouping — slack per clock domain, violations per cell type — awk starts needing arrays and Perl just uses a hash.

4. The Separator Traps

The default separator is a special case in Perl, and three plausible-looking alternatives all behave differently. You do not have to guess which: -MO=Deparse from Chapter 15.7 prints the split Perl actually generated.

Azvya Education Pvt. Ltd.VLSI Mentor
separator-traps.pl
use strict;
use warnings;
use File::Temp qw(tempdir);
use File::Spec;

my $dir = tempdir(CLEANUP => 1);
open my $w, ">", File::Spec->catfile($dir, "indented.txt") or die "cannot write: $!\n";
print $w "U1/Q 0.44 setup\n";          # no leading space
print $w "   U2/D 0.12 hold\n";        # INDENTED, as a real report often is
print $w "U3/CK  -0.03  setup\n";      # double spaces between columns
close $w;

# -F spelled four ways. the shell quoting is part of what is being tested,
# so each is run exactly as a person would type it.
# NOTE the single quotes inside each command: without them the shell eats
# the backslash and Perl receives -Fs rather than -F\s.
my @spec = (
    [ q{(no -F at all)}, q{},        q{perl -lane}         ],
    [ q{-F' '},          q{-F' '},   q{perl -F' ' -lane}   ],
    [ q{-F'\s'},         q{-F'\s'},  q{perl -F'\s' -lane}  ],
    [ q{-F'\s+'},        q{-F'\s+'}, q{perl -F'\s+' -lane} ],
);

printf "%-16s %-22s %s\n", "WRITTEN AS", "DEPARSES TO", "FIELDS PER LINE";
for my $sp (@spec) {
    my ($label, $flag, $prefix) = @$sp;

    my $dep = `perl -MO=Deparse $flag -ane 'print' 2>&1`;   # $flag carries its own quotes
    my ($split) = $dep =~ /(split\([^)]*\))/;
    $split //= "(did not compile)";

    my $out = `cd '$dir' && $prefix 'printf "%d ", scalar(\@F)' indented.txt 2>&1`;
    $out =~ s/\s+\z//;
    $out = "(error)" if $out =~ /Unmatched|syntax/;

    printf "%-16s %-22s %s\n", $label, $split, $out;
}

print "\nand what the fields actually are, for the indented line:\n";
for my $sp (@spec) {
    my ($label, $flag, $prefix) = @$sp;
    my $out = `cd '$dir' && $prefix 'print join("][",\@F) if \$. == 2' indented.txt 2>&1`;
    $out =~ s/\s+\z//;
    printf "  %-16s [%s]\n", $label, $out;
}
Azvya Education Pvt. Ltd.VLSI Mentor
Captured output
WRITTEN AS       DEPARSES TO            FIELDS PER LINE
(no -F at all)   split(' ', $_, 0)      3 3 3
-F' '            split(//, $_, 0)       15 17 19
-F'\s'           split(/\s/, $_, 0)     3 6 5
-F'\s+'          split(/\s+/, $_, 0)    3 4 3

and what the fields actually are, for the indented line:
  (no -F at all)   [U2/D][0.12][hold]
  -F' '            [ ][ ][ ][U][2][/][D][ ][0][.][1][2][ ][h][o][l][d]
  -F'\s'           [][][][U2/D][0.12][hold]
  -F'\s+'          [][U2/D][0.12][hold]

Four spellings, four different split calls, and only one of them is right.

No -F at all gives split(' ', ...) — the single-space string, which is Perl's special case. It splits on runs of whitespace and discards leading whitespace. Every line gives three fields, indented or not. This is what you want for a report, and it is the default precisely because it is.

-F' ' gives split(//, ...) — an empty pattern. This is the trap. The leading space is discarded during option parsing, leaving nothing, and an empty pattern splits between every character. The indented line becomes seventeen one-character fields. Nothing warns; $F[0] is simply U and every column is nonsense.

-F'\s' gives split(/\s/, ...) — one whitespace character. Now each space is its own separator, so the indented line yields three empty fields before the data and the double-spaced line gains empties in the middle.

-F'\s+' gives split(/\s+/, ...) — runs of whitespace. Much closer, but it still does not discard the leading run, so the indented line keeps one empty field at the front and every column is shifted by one.

That last one is the dangerous one, because it is the spelling people reach for and it is nearly right. Three fields become four, $F[1] is the pin instead of the slack, and a total comes out wrong with no error anywhere.

5. What To Remember

  • -a splits each line into @F.
  • @F is zero-based: $F[0] is the first field. The commonest mistake for anyone arriving from awk.
  • $F[-1] is the last field and @F[0,2] is a slice — ordinary Perl arrays.
  • Measured: -a alone already implies the -n loop on 5.34, though the documentation only promises it with -n or -p. Write -an or -ap anyway.
  • -F sets the separator, and it takes a pattern, not just a character.
  • Quote it for the shell — -F'\t', -F'\|'.
  • -F, is not a CSV parser — a quoted field containing a comma breaks it.
  • The awk translation is mechanical: $1→$F[0], NF→scalar @F, NR→$., FILENAME→$ARGV.
  • The default is split ' ' — the special string, meaning runs of whitespace with leading whitespace discarded.
  • Measured: -F' ' deparses to split(//) — an empty pattern that splits every character. The leading space is silently discarded during option parsing.
  • Measured: -F'\s' gives empty fields between every space; -F'\s+' keeps a leading empty field on an indented line.
  • -F'\s+' is the dangerous one — nearly right, so an indented line shifts every column with no error.
  • For whitespace, use no -F at all. Use -MO=Deparse to see which split you actually got.
  • In a script, split ' ' and split /\s+/ are not interchangeable for the same reason.

Chapter 15.9 adds the flag that makes one-liners dangerous as well as useful. -i rewrites the files it reads, and the measured behaviour on failure is better than its reputation — but only with a backup suffix.

Where this fits

Part of the Perl curriculum.