I quickly jotted off a Perl script that would average a few files with just columns of numbers. It involves reading from an array of filehandles. Here is the script:
#!/usr/local/bin/perl
use strict;
use warnings;
use Symbol;
die "Usage: $0 file1 [file2 ...]\n" unless scalar(@ARGV);
my @fhs;
foreach(@ARGV){
my $fh = gensym;
open $fh, $_ or die "Unable to open \"$_\"";
push(@fhs, $fh);
}
while (scalar(@fhs)){
my ($result, $n, $a, $i) = (0,0,0,0);
while ($i <= $#fhs){
if ($a = <$fhs[$i]>){
$result += $a;
$n++;
$i++;
}
else{
$fhs[$i]->close;
splice(@fhs,$i,1);
}
}
if ($n){ print $result/$n . "\n"; }
}
This doesn’t work. If I debug the script, after I initialize @fhs it looks like this:
DB<1> x @fhs
0 GLOB(0x10443d80)
-> *Symbol::GEN0
FileHandle({*Symbol::GEN0}) => fileno(6)
1 GLOB(0x10443e60)
-> *Symbol::GEN1
FileHandle({*Symbol::GEN1}) => fileno(7)
So far, so good. But it fails at the part where I try to read from the file:
DB<3> x $fhs[$i]
0 GLOB(0x10443d80)
-> *Symbol::GEN0
FileHandle({*Symbol::GEN0}) => fileno(6)
DB<4> x $a
0 'GLOB(0x10443d80)'
$a is filled with this string rather than something read from the glob. What have I done wrong?
You can only use a simple scalar variable inside
<>to read from a filehandle.<$foo>works.<$foo[0]>does not read from a filehandle; it’s actually equivalent toglob($foo[0]). You’ll have to use thereadlinebuiltin, a temporary variable, or use IO::File and OO notation.If you weren’t deleting elements from the array inside the loop, you could easily use a temporary variable by changing your
whileloop to aforeachloop.Personally, I think using
gensymto create filehandles is an ugly hack. You should either use IO::File, or pass an undefined variable toopen(which requires at least Perl 5.6.0, but that’s almost 10 years old now). (Just saymy $fh;instead ofmy $fh = gensym;, and Perl will automatically create a new filehandle and store it in$fhwhen you callopen.)