Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Perl one-liner: how to reference the filename passed in when -ne or -pe commandline switches are used

In Perl, it's normally easy enough to get a reference to the commandline arguments. I just use $ARGV[0] for example to get the name of a file that was passed in as the first argument.

When using a Perl one-liner, however, it seems to no longer work. For example, here I want to print the name of the file that I'm iterating through if a certain string is found within it:

perl -ne 'print $ARGV[0] if(/needle/)' haystack.txt

This doesn't work, because ARGV doesn't get populated when the -n or -p switch is used. Is there a way around this?

like image 293
Magnus Avatar asked Feb 05 '12 17:02

Magnus


People also ask

How do you pass a file as an argument in Perl?

If you want to use the two arguments as input files, you can just pass them in and then use <> to read their contents. Alternatively, @ARGV is a special variable that contains all the command line arguments. $ARGV[0] is the first (ie. "string1" in your case) and $ARGV[1] is the second argument.

What is $# ARGV in Perl?

$ARGV. contains the name of the current file when reading from <>. @ARGV. The array ARGV contains the command line arguments intended for the script. Note that $#ARGV is the generally number of arguments minus one, since $ARGV[0] is the first argument, NOT the command name.


1 Answers

What you are looking for is $ARGV. Quote from perlvar:

$ARGV

Contains the name of the current file when reading from <> .

So, your one-liner would become:

perl -ne 'print $ARGV if(/needle/)' haystack.txt

Though be aware that it will print once for each match. If you want a newline added to the print, you can use the -l option.

perl -lne 'print $ARGV if(/needle/)' haystack.txt

If you want it to print only once for each match, you can close the ARGV file handle and make it skip to the next file:

perl -lne 'if (/needle/) { print $ARGV; close ARGV }' haystack.txt haystack2.txt

As Peter Mortensen points out, $ARGV and $ARGV[0] are two different variables. $ARGV[0] refers to the first element of the array @ARGV, whereas $ARGV is a scalar which is a completely different variable.

You say that @ARGV is not populated when using the -p or -n switch, which is not true. The code that runs silently is something like:

while (@ARGV) {
    $ARGV = shift @ARGV;           # arguments are removed during runtime
    open ARGV, $ARGV or die $!; 
    while (defined($_ = <ARGV>)) {  # long version of: while (<>) {
        # your code goes here
    } continue {                   # when using the -p switch
        print $_;                  # it includes a print statement
    }
}

Which in essence means that using $ARGV[0] will never show the real file name, because it is removed before it is accessed, and placed in $ARGV.

like image 75
TLP Avatar answered Oct 19 '22 15:10

TLP