rovf has asked for the wisdom of the Perl Monks concerning the following question:

This example worked, but I wonder whether it worked by accident, or whether this is really portable:
perl -lwe 'my $h1=*STDOUT; open(my $h2, ">", "tmp/xx1.txt") or die; $h +1 eq *STDOUT && print 1; $h2 eq *STDOUT && print 2'
This prints 1.

Background of the question: I have a variable containing a file handle. Depending on which branch the program takes, this variable is either initialized by $fh=*STDOUT or by open($fh,'>',$filename). At some later point in the program, I want to close $fh unless it is connected to STDOUT. The question is how to test this in a portable way.

Using -t $fh is not a solution, because this would test whether $fh is connected to a terminal, which is something different. By experimenting, I found that <c>$f eq *STDOUT<c> seems to do the job. However, doing string equality on a file handle looks a bit arcane to me.

BTW, the test for equality will be done in the same package than the assignment of the file handle (this might be relevant for the way the handle is turned into a string by the equality operator).

-- 
Ronald Fischer <ynnor@mm.st>

Replies are listed 'Best First'.
Re: Testing whether a file handle is attached to STDOUT
by BrowserUk (Patriarch) on Jun 16, 2011 at 10:31 UTC

    You'd probably be better off checking that fileno( $fh ) == 1.


    Examine what is said, not who speaks -- Silence betokens consent -- Love the truth but pardon error.
    "Science is about questioning the status quo. Questioning authority".
    In the absence of evidence, opinion is indistinguishable from prejudice.

      But is *that* portable? It would certainly work on Unix, but how about the Windows family? In particular, Windows 2000, NT and 7 - can we rely on fileno(*STDOUT) always being 1?

      -- 
      Ronald Fischer <ynnor@mm.st>

        Yes, the file numbers for STDIN / STDOUT and STDERR behave the same also on Windows. unistd.h defines them to be 0, 1 and 2 respectively.

        Beware though that if you close one of the three, the file numbers may be reallocated to other filehandles.

        In particular, Windows 2000, NT and 7 - can we rely on fileno(*STDOUT) always being 1?

        Yes. It is a standard C thing, not OS dependant.


        Examine what is said, not who speaks -- Silence betokens consent -- Love the truth but pardon error.
        "Science is about questioning the status quo. Questioning authority".
        In the absence of evidence, opinion is indistinguishable from prejudice.
Re: Testing whether a file handle is attached to STDOUT
by Tanktalus (Canon) on Jun 16, 2011 at 13:51 UTC

    As Corion points out, you can dup stdout to another filehandle, and then close stdout. And then your test would go wonky. Further, you can redirect stdout to a file, either programmatically in your code, or at the command line, myscript.pl > $filename - so what do you want to do in this case?

    My suggestion is to avoid all of this as much as possible. Simply accept the -t test. Yes, it may be that $fh actually is STDERR, and -t will return true. Or $fh could be opened against /dev/tty, so you're still sending stuff to the terminal. Do you really want to tell the difference between myscript.pl (sending to stdout), myscript.pl -f /tmp/my.out (sending to a file), and myscript.pl > /tmp/my.out (same as previous one) and myscript.pl -f /dev/tty > /tmp/my.out (sending to the terminal, but stdout is a file)? I do have to say that I'd find it very odd that -t doesn't do what I want it to do.

      Actually, the -t test will be wrong in most cases, since for my application usually is not connected to a terminal.

      Your arguments are basically correct - thank you for pointing it out. Fortunately, in my case I have control over whether or not stdout will be dup'ed, so this is no problem.
      -- 
      Ronald Fischer <ynnor@mm.st>
Re: Testing whether a file handle is attached to STDOUT
by ikegami (Patriarch) on Jun 16, 2011 at 15:10 UTC
    Depends on what you mean by "STDOUT".
    $ perl -E' my $fh = \*STDOUT; say $fh "test"; say $fh == STDOUT || 0; say $fh eq STDOUT || 0; say $fh == *STDOUT || 0; say $fh eq *STDOUT || 0; say fileno($fh) == 1 || 0; say fileno($fh) == fileno(STDOUT) || 0; ' test 0 0 0 0 1 1
    perl -E' open my $fh, ">&", *STDOUT or die; say $fh "test"; say $fh == STDOUT || 0; say $fh eq STDOUT || 0; say $fh == *STDOUT || 0; say $fh eq *STDOUT || 0; say fileno($fh) == 1 || 0; say fileno($fh) == fileno(STDOUT) || 0; ' test 0 0 0 0 0 0
    $ perl -E' local *STDOUT; open STDOUT, ">&", 1 or die; open my $fh, ">&=", *STDOUT or die; say $fh "test"; say $fh == STDOUT || 0; say $fh eq STDOUT || 0; say $fh == *STDOUT || 0; say $fh eq *STDOUT || 0; say fileno($fh) == 1 || 0; say fileno($fh) == fileno(STDOUT) || 0; ' test 0 0 0 0 0 1
    $ perl -E' local *STDOUT; open STDOUT, ">&", 1 or die; open my $fh, ">&=", 1 or die; say $fh "test"; say $fh == STDOUT || 0; say $fh eq STDOUT || 0; say $fh == *STDOUT || 0; say $fh eq *STDOUT || 0; say fileno($fh) == 1 || 0; say fileno($fh) == fileno(STDOUT) || 0; ' test 0 0 0 0 1 0