perlstdoutfilehandle

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

  • 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;