'What type is STDOUT, and how do I optionally write to it?

Does STDOUT have a "type"?

printf STDERR ("STDOUT = %s\n", STDOUT);
printf STDERR ("\*STDOUT = %s\n", *STDOUT);
printf STDERR ("\\\*STDOUT = %s\n", \*STDOUT);

Produces:

STDOUT = STDOUT
*STDOUT = *main::STDOUT
\*STDOUT = GLOB(0x600078848)

I understand the *main::STDOUT and GLOB(0x600078848) entries. The "bareword" one leaves me curious.

I'm asking because I want to pass a file handle-like argument to a method call. In 'C', I'd use a file descriptor or a File *. I want it to default to STDOUT. What I've done is:

$OUT_FILE_HANDLE = \*STDOUT;
if(@ARGV > 0 ) {
    open($OUT_FILE_HANDLE, ">", "$ARGV[0]") or die $!;
}

It works, but I don't know exactly what I've done. Have I botched up STDOUT? I suspect I have "ruined" (overwritten) STDOUT, which is NOT what I want.

Please pardon the compound question; they seemed related.



Solution 1:[1]

From perlvar:

Perl identifiers that begin with digits or punctuation characters are exempt from the effects of the package declaration and are always forced to be in package main; they are also exempt from strict 'vars' errors. A few other names are also exempt in these ways: [...] STDOUT

So, STDOUT is a global variable containing a pre-opened file handle.

From perlfunc:

If FILEHANDLE is an undefined scalar variable (or array or hash element), a new filehandle is autovivified, meaning that the variable is assigned a reference to a newly allocated anonymous filehandle. Otherwise if FILEHANDLE is an expression, its value is the real filehandle.

Your $OUT_FILE_HANDLE is not undefined, so it is its value, STDOUT, that is being opened. AFAIK, if you open an already open handle, it is implicitly closed first.

There are several ways to do what you want. The first is obvious from the above quote — do not define $OUT_FILE_HANDLE before the open:

if (@ARGV > 0 ) {
  open($OUT_FILE_HANDLE, ">", "$ARGV[0]") or die $!;
} else {
  $OUT_FILE_HANDLE = \*STDOUT;
}
# do stuff to $OUT_FILE_HANDLE

Another is to use select, so you don't need to pass a file handle:

if (@ARGV > 0 ) {
  open($OUT_FILE_HANDLE, ">", "$ARGV[0]") or die $!;
  select $OUT_FILE_HANDLE;
}
# do stuff (without specifying a file handle)
select STDOUT;

Solution 2:[2]

Create a lexical filehandle to be a copy of STDOUT and manipulate that as needed

sub manip_fh { 
    my ($fh) = @_;
    say $fh "hi";                              # goes to STDOUT
    open my $fh, '>', 'a_file.txt' or die $!;  # now it's to a file
    say $fh "hello";
}

open my $fh, '>&', STDOUT;  # via dup2

manip_fh($fh);

say "hi";  # still goes where STDOUT went before being dup-ed (terminal)

This new, independent, filehandle can then be reopened to another resource without affecting STDOUT. See open.

The $OUT_FILE_HANDLE = \*STDOUT; from the question creates an alias and so the STDOUT does indeed get changed when the "new" one changes. You can see that by printing the typeglob

our $NEW = \*STDOUT;  # "our" only for checks here, otherwise better "my"
say *{$main::NEW};    #--> *main::STDOUT

or by printing the IO slot from the symbol table for both

say for *{$main::NEW}{IO}, *{$main::{STDOUT}}{IO};

and seeing (that the object stringifies to) the same (eg IO::File=IO(0x1a8ca50)).

When it's duped using open with mode >& as in the first code snippet (but as global our) it prints *main::NEW, and its IO::File object is not the same as for STDOUT. (Make it a global our so that it is in the symbol table for these checks, but not for real use; it's much better having a my.)

Solution 3:[3]

This part of your question wasn't answered:

The "bareword" one leaves me curious.

An identifier with no other meaning is a string literal that produces itself.[1] For example, foo is the same as 'foo'.

$ perl -e'my $x = foo; print "$x\n";'
foo

This is error-prone, so we use use strict qw( subs ); to prevent this.

$ perl -e'use strict; my $x = foo; print "$x\n";'
Bareword "foo" not allowed while "strict subs" in use at -e line 1.
Execution of -e aborted due to compilation errors.

  1. See this for other meanings Perl could assign.

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Amadan
Solution 2
Solution 3