Question

I have a subroutine that takes a filehandle as an argument. How do I make a filehandle from a file path specified on the command line? I don't want to do any processing of this file myself, I just want to pass it off to this other subroutine, which returns an array of hashes with all the parsed data from the file.

Here's what the command line input I'm using looks like:

$ ./getfile.pl /path/to/some/file.csv

Here's what the beginning of the subroutine I'm calling looks like:

sub parse {
    my $handle = shift;
    my @data   = <$handle>;
    while (my $line = shift(@data)) {
      # do stuff
    }
}
Was it helpful?

Solution

Command line arguments are available in the predefined @ARGV array. You can get the file name from there and use open to open a filehandle to it. Assuming that you want read-only access to the file, you would do it this way:

my $file = shift @ARGV;
open(my $fh, '<', $file) or die "Can't read file '$file' [$!]\n";
parse($fh);

Note that the or die... checks the call open for success and dies with an error message if it wasn't. The built-in variable $! will contain the (OS dependent) error message on failure that tells you why the call wasn't successful. e.g. "Permission denied."

OTHER TIPS

parse(*ARGV) is the simplest solution: the explanation is a bit long, but an important part of learning how to use Perl effectively is to learn Perl.

When you use a null filehandle (<>), it actually reads from the magical ARGV filehandle, which has special semantics: it reads from all the files named in @ARGV, or STDIN if @ARGV is empty.

From perldoc perlop:

The null filehandle <> is special: it can be used to emulate the behavior of sed and awk. Input from <> comes either from standard input, or from each file listed on the command line. Here’s how it works: the first time <> is evaluated, the @ARGV array is checked, and if it is empty, $ARGV[0] is set to "-", which when opened gives you standard input. The @ARGV array is then processed as a list of filenames. The loop

while (<>) {
    ...                     # code for each line
}

is equivalent to the following Perl-like pseudo code:

unshift(@ARGV, '-') unless @ARGV;
while ($ARGV = shift) {
    open(ARGV, $ARGV);
    while (<ARGV>) {
        ...         # code for each line
    }
}

except that it isn’t so cumbersome to say, and will actually work. It really does shift the @ARGV array and put the current filename into the $ARGV variable. It also uses filehandle ARGV internally--<> is just a synonym for <ARGV>, which is magical. (The pseudo code above doesn’t work because it treats <ARGV> as non-magical.)

You don't have to use <> in a while loop -- my $data = <> will read one line from the first non-empty file, my @data = <>; will slurp it all up at once, and you can pass *ARGV around as if it were a normal filehandle.

This is what the -n switch is for!

Take your parse method, and do this:

#!/usr/bin/perl -n

#do stuff

Each line is stored in $_. So you run

./getfile.pl /path/to.csv

And it does this.

See here and here for some more info about these. I like -p too, and have found the combo of -a and -F to be really useful.

Also, if you want to do some extra processing, add BEGIN and end blocks.

#!/usr/bin/perl -n

BEGIN {
  my $accumulator;
}

# do stuff

END {
  print process_total($accumulator);
}

or whatever. This is very, very useful.

Am I missing something or are you just looking for the open() call?

open($fh, "<$ARGV[0]") or die "couldn't open $ARGV[0]: $!";
do_something_with_fh($fh);
close($fh);
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top