Why are Perl's $. and $ARGV behaving strangely after setting @ARGV and using <>
Asked Answered
S

1

5

I wrote a perl program to take a regex from the command line and do a recursive search of the current directory for certain filenames and filetypes, grep each one for the regex, and output the results, including filename and line number. [ basic grep + find functionality that I can go in and customize as needed ]

cat <<'EOF' >perlgrep2.pl
#!/usr/bin/env perl
$expr = join ' ', @ARGV;

my @filetypes = qw(cpp c h m txt log idl java pl csv);
my @filenames = qw(Makefile);

my $find="find . ";
my $nfirst = 0;
foreach(@filenames) {
    $find .= " -o " if $nfirst++;
    $find .= "-name \"$_\"";
}
foreach(@filetypes) {
    $find .= " -o " if $nfirst++;
    $find .= "-name \\*.$_";
}

@files=`$find`;

foreach(@files) {
    s#^\./##;
    chomp;
}

@ARGV = @files;

foreach(<>) {
    print "$ARGV($.): $_" if m/\Q$expr/;
    close ARGV if eof;
}
EOF

cat <<'EOF' >a.pl
print "hello ";
$a=1;
print "there";
EOF

cat <<'EOF' >b.pl
print "goodbye ";
print "all";
$a=1;
EOF

chmod ugo+x perlgrep2.pl
./perlgrep2.pl print

If you copy and paste this into your terminal, you will see this:

perlgrep2.pl(36): print "hello ";
perlgrep2.pl(0): print "there";
perlgrep2.pl(0): print "goodbye ";
perlgrep2.pl(0): print "all";
perlgrep2.pl(0):     print "$ARGV($.): $_" if m/\Q$expr/;

This is very surprising to me. The program appears to be working except that the $. and $ARGV variables do not have the values I expected. It appears from the state of the variables that perl has already read all three files (total of 36 lines) when it executes the first iteration of the loop over <>. What's going on ? How to fix ? This is Perl 5.12.4.

Shay answered 22/5, 2013 at 16:54 Comment(0)
P
10

You're using foreach(<>) where you should be using while(<>). foreach(<>) will read every file in @ARGV into a temporary list before it starts iterating over it.

Plenitude answered 22/5, 2013 at 17:10 Comment(2)
Thank you! I must say I've been reading a lot of perl documentation recently and I did not pick that up.Shay
Ah, so "for" evaluates <> in list context, which reads the whole file(s) into a list, and while evaluates <> in scalar context which reads the next line.Shay

© 2022 - 2024 — McMap. All rights reserved.