in reply to Reassign STDOUT/STDERR contents to a variable

Is there a direct way to redirect what would go to STDOUT and STDERR to variables

Yes, you can open STDOUT/STDERR to $variables. For example

my $script_content = 'print STDERR "foo"'; open(SAVEERR, ">&STDERR"); close(STDERR); open(STDERR, '>', \my $buf) or die $!; eval $script_content; print "buf = $buf\n"; # "buf = foo"

But note that this technique does not capture what external programs further down the line might write to stderr (just in case that's what you're having in mind).

I.e., if $script_content was something like 'system "cat does-not-exist"', $buf would not hold cat's error message "cat: does-not-exist: No such file or directory". Reason is that file handles opened to scalar variables are Perl-internal (indicated by fileno returning -1), not accessible from processes outside of the Perl program.

Replies are listed 'Best First'.
Re^2: Reassign STDOUT/STDERR contents to a variable
by lothos (Novice) on Sep 25, 2009 at 18:02 UTC
    Yep. That works for STDOUT/STDERR. Same thing for STDIN?
    my $stdin = 'Some input text here'; open(STDIN,'<', \$stdin);

    That give about 10 pages of errors that look like a manpage for PerlIO.

    Thank you,

      Should work, too, in principle, e.g.

      my $stdin = "Some input text here\n"; close STDIN; open(STDIN,'<', \$stdin) or die $!; while (my $line = <STDIN>) { print "got line: $line"; } __END__ got line: Some input text here

      What are those 10 pages complaining about? :)

        Yep. Your test code works, but in my script I get this. Ignore the Host not found: on the lines. That's output from my script. Looks like a perlio man page.

        I also failed to mention that the input I'm speaking of might, or might not be binmode. This works as expected:

        open(FILE,'>' . $stdin_file); binmode(FILE); print FILE $script_input; close(FILE); close(STDIN); open(STDIN, '<',$stdin_file);
        This:
        #open(FILE,'>' . $stdin_file); #binmode(FILE); #print FILE $script_input; #close(FILE); close(STDIN); open(STDIN, '<',\$script_input);
        Results in my output being:

      AHA! I just ran into the same problem in some code I'm writing: I had assigned to the "in memory" variable after opening it. Turns out you can only concatenate to it, otherwise you have to seek to the beginning. Some examples:

      # Save old STDIN and STDOUT open my $oldin, '<&', \*STDIN or die "Can't dup STDIN:$!"; open my $oldout, '>&', \*STDOUT or die "Can't dup STDOUT:$!"; # set up new STDIN my $invar = "foo\n"; close STDIN; open STDIN, '<', \$invar or die "Can't open scalar:$!"; # set up new STDOUT my $outvar; close STDOUT; open STDOUT, '>', \$outvar or die "Can't open scalar:$!"; # Try it out! print <>; print STDERR $out; # prints "foo" # But beware: # $invar = "bar\n"; #BAD! try this instead: $invar .= "bar\n"; print STDERR <>; #Would have given your manpage. Now works! # This might work, too, but I'm not sure: $invar = "baz\n"; seek STDIN, 0, 0; print STDERR <>; #Not tested, should print "baz\n"; # Another caveat about STDOUT: $outvar = ""; #This does not do what you think. print "ding\n"; print STDERR $outvar; #prints same as "foo\nding\n" # instead, do this: seek STDOUT, 0, 0; print "dong\n"; print STDERR $outvar; # this does print "dong\n" only. # Now restore old filehandles: close STDIN; open STDIN, '<&', $oldin or die "Can't redup STDIN\n"; close $oldin; close STDOUT; open STDOUT, '<&', $oldout or die "Can't redup STDOUT\n"; close $oldout;

      UPDATE: the seek on STDIN works just fine. I decided to go that route because it scales better (always concatenating to my input scalar would keep increasing my memory usage.

      print pack("A25",pack("V*",map{1919242272+$_}(34481450,-49737472,6228,0,-285028276,6979,-1380265972)))

        Good stuff. Doesn't seem to work for my eval though..

        print "Script content: $script_content\n"; print "Script input: $script_input\n"; open(SAVEOUT, ">&STDOUT"); open(SAVEERR, ">&STDERR"); close(STDIN); open(STDIN, '<',\my $stdin); #seek STDIN,0,0; # with or without, no diff. $stdin .= $script_input; #seek STDIN,0,0; # with or without, no diff. close(STDOUT); open(STDOUT,'>' , \my $stdout); close(STDERR); open(STDERR,'>' , \my $stderr); select(STDERR); $| = 1; select(STDOUT); $| = 1; eval $script_content; my $result_code = $@; close(STDOUT); close(STDERR); open(STDOUT, ">&SAVEOUT"); open(STDERR, ">&SAVEERR"); if ( ! $stderr ) { $stderr = ''; } # my db doesn't like nulls. if ( ! $stdout ) { $stdout = ''; } print "STDERR:" . $stderr; print "STDOUT:" . $stdout;

        In all variations where I'm using STDIN that is tied to a variable, I still get the manpage. Argh.

        I'm thinking the difference is that in your scenario you can do your writes to STDIN after the process has it. I think I have to have it set up prior to my eval, or my read of stdin within my evaled script will just return as it would get EOF immediately.

      You need to close STDIN first (same goes for STDOUT and STDERR). Then reopen to wherever.

      print pack("A25",pack("V*",map{1919242272+$_}(34481450,-49737472,6228,0,-285028276,6979,-1380265972)))
        yeah. I do. I just missed it in the 'making it brief' copy. I'm copying from a 400 line script with all sorts of nasty other stuff. Thanks,