Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why is @ARGV undefined inside this Perl one-liner?

Tags:

perl

A fellow Stackoverflower tried to use @ARGV in his END block but was unable to.

Why is it that @ARGV is only defined inside the BEGIN block with the following one-liner:

$ perl -lne 'BEGIN{ print "BEGIN"  if @ARGV }
                    print "MIDDLE" if @ARGV }
                  { print "END"    if @ARGV  ' file
  BEGIN

perldoc perlrun doesn't shed any light on the matter. What's going on here?

like image 527
Zaid Avatar asked Jan 18 '23 22:01

Zaid


2 Answers

First, arrays cannot be undefined. You are checking if the array is empty. To understand why it's being emptied, you need to understand -n. -n surrounds your code with

LINE: while (<>) {
   ...
}

which is short for

LINE: while (defined($_ = <ARGV>)) {
   ...
}

ARGV is a magical handle that reads through the files listed in @ARGV, shifting out the file names as it opens them.

$ echo foo1 > foo
$ echo foo2 >>foo

$ echo bar1 > bar
$ echo bar2 >>bar

$ echo baz1 > baz
$ echo baz2 >>baz

$ perl -nlE'
    BEGIN { say "Files to read: @ARGV" }
    say "Read $_ from $ARGV. Files left to read: @ARGV";
' foo bar baz
Files to read: foo bar baz
Read foo1 from foo. Files left to read: bar baz
Read foo2 from foo. Files left to read: bar baz
Read bar1 from bar. Files left to read: baz
Read bar2 from bar. Files left to read: baz
Read baz1 from baz. Files left to read:
Read baz2 from baz. Files left to read:

Keep in mind that BEGIN blocks are executed as soon as they are compiled, so the <ARGV> hasn't yet been executed when the BEGIN block is being executed (even though it appears earlier in the program), so @ARGV hasn't been modified yet.

-n is documented in perlrun. ARGV, @ARGV and $ARGV are documented in perlvar.

like image 117
ikegami Avatar answered Jan 22 '23 18:01

ikegami


A BEGIN block runs before anything else. At that point, @ARGV has everything being passed and a test for non-emptiness returns true. When the END block runs, the elements of the original @ARGV have been shifted away by the implicit while(<>) {...} loop generated by the '-n' switch. Since there is nothing left, the empty @ARGV tests false. Change the END block to:

{print "END" if defined @ARGV}

As each element of @ARGV is shifted, it is stored in $ARGV. Hence, the block could be also rewritten:

{print "END" if $ARGV}
like image 21
JRFerguson Avatar answered Jan 22 '23 18:01

JRFerguson