I want to make a quick script that writes to a file if a file is given, or stdout if no file is given. It would be much easier for me to do this if I could start the script by p
You could always just write the regular output to STDOUT
and your debug statements to STDERR
. Then if the user wants the output to go to a file they can just redirect STDOUT to the file on the command line.
Some ways (globs):
# Requires 2-arg open, unfortunately
open(OUTPUT, "> ".($output || '-')) or die;
if ($output) {
open(OUTPUT, '>', $output) or die;
} else {
*OUTPUT = *STDOUT;
}
if ($output) {
open(OUTPUT, '>', $output) or die;
} else {
open(OUTPUT, '>&', \*STDOUT) or die;
}
Some ways (lexical var):
# Requires 2-arg open, unfortunately
open(my $fh, "> ".($output || '-')) or die;
my $fh;
if ($output) {
open($fh, '>', $output) or die;
} else {
$fh = \*STDOUT;
}
my $fh;
if ($output) {
open($fh, '>', $output) or die;
} else {
open($fh, '>&', \*STDOUT) or die;
}
Some ways (other):
# Changes the default handle for <print "foo">.
if ($output) {
open(OUTPUT, '>', $output) or die;
select(OUTPUT);
}
Nevermind. Figured it out. Throwing in an ampersand does the trick. Full working code is:
open( OUTPUT, ( $output ? ">$output" : ">&STDOUT" ) );
-
is magic. Follow the convention or you violate the principle of least surprise.
use autodie qw(:all);
use Getopt::Long qw(GetOptions);
GetOptions(\my %opt, 'out=s') or die 'usage';
die 'usage' unless $opt{out};
open my $handle, ">$opt{out}";
perl foo -o blahblah # opens file blahblah for writing
perl foo -o - # goes to STDOUT
Related: Are there reasons to ever use the two-argument form of open(...) in Perl?
For the reader's sake (since OP already "got it working"):
The Perl documentation, "perlopentut" (perldoc perlopentut) gives examples of opening an alternate filehandle directed to STDOUT, but it uses bareword filehandles, and the two-arg version of open
, both of which are somewhat ancient. Damian Conway's book "Perl Best Practices" (as well as Perl::Critic, which is based on Damian's book) emphasizes using three arg opens and lexical filehandles (my $foo
style filehandles). chromatic's "Modern Perl" also briefly mentions using lexical filehandles, and discourages barewords where practical. The three arg open
is considered to be a safer form, as it limits exposure to shell injections. While the specific case here is low risk, it's still a good habit to default to the three-arg open
.
It takes careful reading of the examples shown in Perl's open
documentation (perldoc -f open), and a little interpretation to notice the correct placement of the ampersand character in opening a duplicate filahandle directed to STDOUT
when using the three-arg version of open
. One might errantly assume that this should work: open my $fh, '>', '&STDOUT' or die $!;
, mostly because it looks like a format we're used to seeing (ampersand preceding a symbol name). But that syntax isn't what open
needs, and we're used to seeing it in an entirely different context: certain subroutine calls.
The correct way to open a dupe to STDOUT
with the three arg open
is to combine the >
and the &
into the same second argument, and leave STDOUT bare, like this:
use Modern::Perl;
open my $fh, '>&', STDOUT or die "Bleah: $!";
say $fh 'Printing alternate filehandle "$fh" worked.';
say 'Implicitly printing to "STDOUT" also works.';
Or passing STDOUT
to open
as a reference to the typeglob:
open my $fh, '>&', \*STDOUT or ...