http://qs321.pair.com?node_id=11128967

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

In an ancient part of the code at work, we use
open my $output, '>-' or die $!;

Perl::Critic complains about it and wants us to replace it with a three argument variant. But, what's the 3-arg variant of >-?

I thought

open my $output, '>&', *STDOUT or die $!;
might be the same, but it isn't, cf.
open my $output, '>-' or die $!; close *STDOUT; print {$output} "This fails with Bad file descriptor";
versus
open my $output, '>&', *STDOUT or die $!; close *STDOUT; print {$output} "This prints OK\n";

Then I tried with

my $output = *STDOUT;
but it's different, too:
close STDOUT; print {$output} 'Besides Bad file descriptor, this will also warn "print() on clos +ed filehandle STDOUT"';

Is there a three-argument version of open that behaves the same as '>-'?

Update: Here's a SSCCE. Closing STDOUT is a global state, so call this with an argument (1, 2, 3, 4) to test a particular implementation.

#!/usr/bin/perl use strict; use warnings; sub original { open my $output, '>-' or die $!; $output } sub assign { my $output = *STDOUT; $output } sub dup { open my $output, '>&', *STDOUT or die $!; $output } sub fn { open my $output, '>>&=', *STDOUT->fileno or die $!; $output } use Test::More; sub test { my ($open) = @_; my @W; local $SIG{__WARN__} = sub { push @W, @_ }; my $output = $open->(); close *STDOUT; my $v = print {$output} "abc\n"; isnt $v, 1, 'fails'; like $!, qr/Bad file descriptor/, 'Exception'; is scalar @W, 0, 'no warnings'; chomp, diag "($_)" for @W; done_testing(); } my %dispatch = (1 => \&original, 2 => \&assign, 3 => \&dup, 4 => \&fn); my $what = shift; my $code = $dispatch{$what} or die 'Not associated'; test($code);

map{substr$_->[0],$_->[1]||0,1}[\*||{},3],[[]],[ref qr-1,-,-1],[{}],[sub{}^*ARGV,3]

Replies are listed 'Best First'.
Re: Three argument version of open '>-' (de-critic)
by LanX (Saint) on Mar 02, 2021 at 02:16 UTC
    > Perl::Critic complains about it and wants us to replace it with a three argument variant. But, what's the 3-arg variant of >-?

    From a pragmatic point of view, I'd say:

    Adjust the Perl::Critic rule!

    I mean it's an interesting question, but probably it's not possible to duplicate all these exact implementation details.

    This particular 2 args syntax OTOH is documented and legit.

    Cheers Rolf
    (addicted to the Perl Programming Language :)
    Wikisyntax for the Monastery

    update

    and by all communist gods, why would you like to have a duplicate of STDOUT which fails globally without warning?

    Probably this feature is simply broken.

      Seconding this: Perl::Critic is simply wrong here. The primary risk of two-argument open is the possibility of a "strange" filename improperly triggering the magic (How about a "file" named 'rm -rf / |'?), but in this case the filename is a literal constant and that is not possible.

      This is a bug in Perl::Critic — your particular use of the two-argument syntax is fine.

Re: Three argument version of open '>-'
by Fletch (Bishop) on Mar 01, 2021 at 22:23 UTC

    If the OS provides it perhaps explicitly opening /dev/stdout as the filename? Or maybe no because that's going to behave like your "prints OK" block because it'd return a new descriptor on the device.

    Slightly ebil but maybe open( my $output, ">>&=", STDOUT->fileno ) would get it? (I want to say that should create a new handle instance pointing to the same underlying descriptor which seems to be what >- was doing).

    The cake is a lie.
    The cake is a lie.
    The cake is a lie.

Re: Three argument version of open '>-'
by LanX (Saint) on Mar 01, 2021 at 22:21 UTC
    Maybe try open $fh, '>&', STDOUT;

    behavior looks consistent when autoflush is activated.

    Otherwise please use a similar code-template for a SSCCE.

    use v5.12; use warnings; use Data::Dump qw/pp dd/; sub tst { my $vers = shift; open STDOUT, ">-"; # restore STDOUT $|=1; say STDERR "********* testing $vers\n"; my $fh; open $fh, ">-" if $vers eq "open2"; $fh = \*STDOUT if $vers eq "copyref"; open $fh, '>&', STDOUT if $vers eq "redirect"; $fh->autoflush; say "$vers 1"; close STDOUT; say $fh "$vers 2"; say "$vers 3"; } tst("redirect"); tst("open2"); tst("copyref");

    -*- mode: compilation; default-directory: "d:/tmp/pm/" -*- Compilation started at Mon Mar 1 23:21:00 C:/Strawberry/perl/bin\perl.exe -w d:/tmp/pm/redirect_STDOUT.pl ********* testing redirect redirect 1 redirect 2 say() on closed filehandle STDOUT at d:/tmp/pm/redirect_STDOUT.pl line + 32. ********* testing open2 open2 1 open2 2 say() on closed filehandle STDOUT at d:/tmp/pm/redirect_STDOUT.pl line + 32. ********* testing copyref copyref 1 say() on closed filehandle STDOUT at d:/tmp/pm/redirect_STDOUT.pl line + 30. say() on closed filehandle STDOUT at d:/tmp/pm/redirect_STDOUT.pl line + 32. Compilation finished at Mon Mar 1 23:21:00

    Cheers Rolf
    (addicted to the Perl Programming Language :)
    Wikisyntax for the Monastery

      OP updated witn a SSCCE.

      map{substr$_->[0],$_->[1]||0,1}[\*||{},3],[[]],[ref qr-1,-,-1],[{}],[sub{}^*ARGV,3]
        Thanks, it's clearer now, you want that the "copy FH" fails if STDOUT is closed, but without emitting a warning.

        Sorry, no clue yet...

        update

        BTW: you can automatize your test-runs with an exec $0 #Test if @ARGV is empty :)

        and maybe you should also consider testing different states of autoflush.

        Cheers Rolf
        (addicted to the Perl Programming Language :)
        Wikisyntax for the Monastery

Re: Three argument version of open '>-'
by LanX (Saint) on Mar 01, 2021 at 21:55 UTC
    same behavior for me

    DB<47> open $fh,">-" DB<48> say $fh "bla" bla DB<49> say "bla" bla DB<50> close STDOUT DB<51> say $fh "bla" DB<52>

    copy solution

    DB<53> say "bla" bla DB<54> $fh=\*STDOUT # copy DB<55> say $fh "bla" bla DB<56> close STDOUT DB<57> say $fh "bla" DB<58>

    what am I missing?

    EDIT: Both approaches warn under warnings if STDOUT was closed.

    UPDATE

    I just remembered that the debugger does scary redirections under the hood, so these tests may not be reliable.

    Cheers Rolf
    (addicted to the Perl Programming Language :)
    Wikisyntax for the Monastery