tags:

views:

176

answers:

2

I have a perl program that takes input and output file arguments, and I'd like to support the convention of using "-" to specify standard input/output. The problem is that I can't just open the file name, because open(my $input, '<', '-') opens a file called -, not standard input. So I have to do something like this:

my $input_fh;
if ($input_filename eq '-') {
    # Special case: get the stdin handle
    $input_fh = *STDIN{IO};
}
else {
    # Standard case: open the file
    open($input_fh, '<', $input_filename);
}

And similarly for the output file. Is there any way to do this without testing for the special case myself? I know I could hack the ARGV filehandle to do this for input, but that won't work for output.

Edit: I've been informed that the 2-argument form of open actually does the magic I'm looking for. Unfortunately, it also does some creative interpretation in order to separate the filename from the mode in the second argument. I guess the 3-argument form of open is 100% magic-free -- it always opens the exact file you tell it to. I'm asking if I can have a single exception for "-" while still handling every other file name unambiguously.

Should I just stick my code from above into a subroutine/module and stop whining?

+5  A: 

Use the two-arguments form instead:

open ($input_fh, "< " . $input_filename);

From man perlfunc:

In the 2-arguments (and 1-argument) form opening "'-'" opens STDIN and opening "'>-'" opens STDOUT.

Note that the < mode is optional in the 2-arguments form, so "< -" is perfectly legal.

Daniel
Doesn't two-argument open suffer from ambiguity issues, in cases when files begin with `<` or `>`? Or does the concatenation that you showed above (`'<' . $filename`) always eliminate the ambiguity?
Ryan Thompson
@Ryan I wasn't aware that was a requirement. Just introduce a space between mode and filename, as shown in the edited answer.
Daniel
Well, in theory you could have a file whose name starts with a space. The problem is that the 2-argument open has to interpret the file name no matter what. I like the safety of the 3-arg open in that it will take the file name literally no matter what.
Ryan Thompson
@Ryan You can't take the file name literally except in the case of "-", can you? :-) Perhaps you can quote white spaces.
Daniel
I guess the 2-arg open is the best answer to the question I asked. The solution that I'm going to use is to simply encapsulate the behavior that I want into my own utility subroutine.
Ryan Thompson
A: 

I think I found what I'm looking for. There's a nifty module called IO::All that does what I want and more.

For Debian/Ubuntu/similar users, the package is called libio-all-perl. I wanted to make that a clickable link to "apt:libio-all-perl", but apparently only http links are allowed here.


Edit

There's also Iterator::Diamond, which implements the tunable magic that I'm asking for. It's designed for replacing the "diamond" operator <>, but it will probably do what I ask if called with just a single file (presumably with some overhead, but who cares?).

Ryan Thompson
Actually, neither of these modules provides full compatibility with simple filehandles.
Ryan Thompson