0

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.

2 Answers 2

4

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>) {
       ...
    }
}
Sign up to request clarification or add additional context in comments.

4 Comments

Is assigning a scalar, - to an array kosher?
@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.
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.
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.
1

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;

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.