For cases where one wishes to avoid the useful while(<>) syntax and manage file input manually, how does one handle the operation of reading from a list of files and/or STDIN? To read from files, one can simply iterate through @ARGV, opening each element in turn (e.g., my $file=shift @ARGV; open(my $fh,'<',$file); while(<$fh>) {...}; close($fh);. To read from standard input, one can simply use while(<STDIN>) { ...}. However, assuming the programmer expects similar types of data to be provided through STDIN and file arguments, the body of code within each while loop would have to be duplicated. I have tried unsuccessfully to assign STDIN to a filehandle (e.g., my $fh = \*STDIN or my $fh = *STDIN{IO}, each of which I have seen suggested elsewhere on this website). In essence, I would like to iterate through all files as well as STDIN and treat the input from each identically, but without using the handy while(<>) syntax. Could you please sketch a solution to this problem? Thank you.
Add a comment
|
2 Answers
With two-arg open (like <> uses), you could do
@ARGS = '-' if !@ARGV;
for my $qfn (@ARGV) {
open($fh, $qfn);
while (<$fh>) {
...
}
}
Which three-arg open, I might do
@ARGV = \*STDIN if !@ARGV;
for my $qfn (@ARGV) {
my $fh;
if (ref($qfn)) {
$fh = $qfn;
} else {
open($fh, '<', $qfn);
}
while (<$fh>) {
...
}
}
4 Comments
Mr. Llama
Is assigning a scalar,
- to an array kosher?ikegami
@Mr. Llama, A list consisting of a single scalar is still a list, since a list simply means "zero or more scalars". As long as the RHS of the assignment evaluates to zero or more scalars (which all expressions do), you're ok.
user001
Thank you — your answer is very helpful (I opted for the three-argument solution). Could you please also show how one would handle the (rare) case in which the user simultaneously pipes in data to the script and provides files as arguments? I believe the
while (<>) syntax can handle such a situation. Can one simply push(@ARGV,\*STDIN) and detect later whether it is devoid of content (ignoring it, if so)? Finally, I am curious to know the significance of the abbreviation qfn.alexis
script file1 file2 - file4 will read stdin as the third file, without help fromthis solution. This is the only reasonable way to do it, since stdin is never "devoid of content": It is always open (unless you explicitly close it), and a program that tries to read from it will wait for terminal input.Another way (if you can use CPAN modules) is to use IO::All.
Read a file into a scalar variable:
my $content1 < io('file1');
Another way to do it with IO::ALL:
my $content2 = io('file1')->slurp;
Or if you want it in an array, with each line as an element:
my @lines = io('file1')->slurp;