问题
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
}
}
回答1:
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."
回答2:
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 loopwhile (<>) { ... # 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 filehandleARGV
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.
回答3:
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.
回答4:
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);
来源:https://stackoverflow.com/questions/189293/how-do-i-get-a-filehandle-from-the-command-line