In my Perl script, I would like to process lines from either STDIN
or a given file, if specified, as common with Linux/UNIX command line utilities.
To this end, I have the following section in my script (simplified for the post):
use strict;
use warnings;
my $in = \*STDIN;
open $in, '<', $ARGV[0] or die if (defined $ARGV[0]);
print while (<$in>);
Essentially, I define $in
to be a reference to the STDIN
typeglob, so normally, if no argument is specified, the script does print
for each line of <STDIN>
. So far, so good.
If $ARGV[0]
is defined however, I would like to read lines from that. That is what the second meaningful line purports to do. However, it seems that no lines are processed when ran with an argument.
I noticed that after my conditional call to open
, $in
does not change, even when I expect it to;
my $in = \*STDIN;
print $in, "\n";
open $in, '<', $ARGV[0] or die if (defined $ARGV[0]);
print $in, "\n";
yields
GLOB(0xaa08b2f4f28)
GLOB(0xaa08b2f4f28)
even when $ARGV[0]
is defined. Does open
not work when the first variable passed is already referring to a filehandle?
The relevant documentation does include the following
About filehandles
The first argument to open, labeled FILEHANDLE in this reference, is usually a scalar variable. (Exceptions exist, described in "Other considerations", below.) If the call to open succeeds, then the expression provided as FILEHANDLE will get assigned an open filehandle. That filehandle provides an internal reference to the specified external file, conveniently stored in a Perl variable, and ready for I/O operations such as reading and writing.
Based on this alone, I do not see why my code would not work.
CodePudding user response:
That's precisely what the null filehandle <> does
Input from
<>
comes either from standard input, or from each file listed on the command line.
So all you need is
while (<>) {
...
}
(see the rest of what docs say about it)
Another, in some cases safer option, is to use a double diamond bracket
while (<<>>) { }
Using double angle brackets inside of a while causes the open to use the three argument form (with the second argument being
<
), so all arguments inARGV
are treated as literal filenames (including "-"). (Note that for convenience, if you use<<>>
and if@ARGV
is empty, it will still read from the standard input.)
(again, please see the rest of what docs say)
Added
As for the direct question -- yes, one can reassign a filehandle just as you try to do.
But the ... or die if ...
syntax is wrong as one cannot chain conditionals like that.
However, I cannot reproduce the shown behavior as your code actually works for me (on 5.16 and 5.30 on Linux). My best guess then is that such code results in an "undefined behavior" and we get unpredictable and inconsistent behaviors.
While <<>>
does what is asked, if you do need to have a filehandle at a minimum can fix that by adding parenthesis
(open $in, '<', $ARGV[0] or die $!) if defined $ARGV[0];
But I'd recommend writing a nice and readable test instead of cramming it into one statement.
CodePudding user response:
You want to use the magical ARGV
file handle, which does exactly what you want.
And the safest way to read from is is the following:
while (<<>>) {
...
}
You were going for something like this:
my $in_fh;
if ( @ARGV ) {
open( $in_fh, "<", $ARGV[0] )
or die( "Can't open `$ARGV[0]`: $!\n" );
} else {
$in_fh = \*STDIN;
}
while (<$fh_in>) {
...
}
However, unlike unix tools, this only reads from the first file provided. Using the first solution reads from each file provided.