SPUG: "last" behaving oddly in -n loop

Ben Reser ben at reser.org
Wed Aug 20 18:58:02 CDT 2003


On Wed, Aug 20, 2003 at 01:55:09PM -0700, Tim Maher wrote:
> SPUGsters,
> 
> I'm confused about how "last" is being handled with a -n
> (implicit) loop, which is very different from the way "next"
> is being handled.  I'd appreciate any insights!
> 
> Here's the problem in a nutshell:
> 
> $ cat last2
> print "$.: $ARGV" and last LINE ; END{print "File is $ARGV\n"}
> 
> $ perl -wln last2 motd1 motd2
> 1: motd1
> File is motd1
> $
> 
> Why didn't it process the second file, motd2, as happens with "next"?
> It's acting like last is magically allowed to break out of the outer
> (invisible) foreach loop, while next only affects the inner (invisible)
> while loop.

You're assuming that it is expanding to something roughly like this:
BEGIN { $^W = 1; }
BEGIN { $/ = "\n"; $\ = "\n"; }
END { print "File is $ARGV\n"; }

unshift (@ARGV, '-') unless @ARGV;
while ($#ARGV >= 0) {
  $ARGV = shift;
  open (ARGV, $ARGV);
  LINE: while (defined($_ = <ARGV>)) {
    chomp $_;
    print "$.: $ARGV" and last LINE;
  }
}

This is probably a pretty common assumption since the documention for
the null file handle gives a code example that roughly would fit that.
However, it is simply a psuedo code and you can't rely on perl to
actually implement things that particular way (in fact it doesn't).  A
careful reading of the documentation hints at that but perhaps the
documentation could be more explicit about that.

Rather perl is implementing things as Deparse is showing and <ARGV> is
magical.  There is an implicit loop but is not implemented as a perl
expansion but treated entirely special.  From your programs view <ARGV>
provides it the contents of the file currently opened as ARGV and then
the contents of each following file named in @ARGV or STDIN if no files
are listed in @ARGV.

A good way to see how <ARGV> is completely special is to try and do
something like this:
 perl -e 'open ARGV, 'motd2'; while (<ARGV>) { print;}' motd1 motd2

If you run this you'll see that you get the output for motd2, then
motd1, then motd2 again.  

This is slightly different than how the documentation says it behaves.
If it behaved exactly as the documentation was written then my expansion
of your code above would not have worked at all.  I'm not sure if it has
always behaved this way or if this was a change that was made at some
point along the way to allow such code to actually work, despite the
magical properties of <ARGV>.

To add to the confusion perl acutally does assume loops around your
programs when using -n, and in that case it that loop really exists and
you can even control its flow with the normal flow control keywords.
While the documentation in -n says it does assume that loop, the
documentation for the null filehandle only says it provides you
something like such a loop.

Andrew Sweger and John W. Krahn already explained how to get it to
behave the way you want so I won't repeat that.

HTHs

-- 
Ben Reser <ben at reser.org>
http://ben.reser.org

"What upsets me is not that you lied to me, but that from now on I can
no longer believe you." -- Nietzsche



More information about the spug-list mailing list