Is there a way to capture a subroutine's print output to a variable so I can send it to stderr instead?

后端 未结 4 1023
离开以前
离开以前 2021-01-12 08:50

Suppose we have:

sub test {
        print \"testing\\n\";
}

If there is a case where I want to have it print to stderr instead of stdout, i

相关标签:
4条回答
  • 2021-01-12 09:00

    Meanwhile, you can also "capture a subroutine's print output to a variable."

    Just pass a scalar ref to open:

    #! /usr/bin/env perl
    use common::sense;
    use autodie;
    
    sub tostring (&) {
      my $s;
      open local *STDOUT, '>', \$s;
      shift->();
      $s
    }
    
    sub fake {
      say 'lalala';
      say 'more stuff';
      say 1 + 1, ' = 2';
      say for @_;
    }
    
    for (tostring { fake(1, 2, 3) }) {
      s/\n/\\n/g;
      say "Captured as string: >>>$_<<<";
    }
    

    Output:

    Captured as string: >>>lalala\nmore stuff\n2 = 2\n1\n2\n3\n<<<
    
    0 讨论(0)
  • 2021-01-12 09:12

    This work for me

    local *STDOUT;
    open(STDOUT, ">", \$Result);
    &test();
    print $Result;
    
    0 讨论(0)
  • 2021-01-12 09:17

    Yes there is. print sends its output to the "selected" filehandle, which is usually STDOUT. But Perl provides the select function for you to change it.

    select(STDERR);
    &test;           # send output to STDERR
    select(STDOUT);  # restore default output handle
    

    The select function returns the previously selected filehandle, so you can capture it and restore it later.

    my $orig_select = select(STDERR);
    &test;
    select($orig_select);
    
    0 讨论(0)
  • 2021-01-12 09:23

    Perl's dynamic scoping via local() is not often used, but this strikes me as a good application for it:

    test(); # to stdout
    {
        open(local *STDOUT, ">&STDERR") or die "dup out to err: $!";
        test(); # to stderr, locally calling it "STDOUT"
    }
    test(); # to stdout again
    

    The call to test() in the block above -- as well as to anything that test() itself might call -- will have STDOUT dynamically scoped to your duplicate of STDERR. When control leaves the block, even if by die()ing, STDOUT will be restored to whatever it was before the block

    Generalized:

    sub out2err(&) {
      my $user_block = shift;
      open(local *STDOUT, ">&STDERR") or die $!;
      $user_block->();
    }
    
    test();             # to stdout
    out2err { test() }; # to stderr
    test();             # to stdout
    
    0 讨论(0)
提交回复
热议问题