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

I have a contiuously running script that executes pieces of perl code stored in mysql. I control the input scripts so security and namespace polution are not really a concern, although ideas about controlling that as well are welcome.

I currently write out what I want STDIN to be to a file, and the reassign STDIN to that file, restoring STDIN when done. Same goes for STDERR and STDOUT.

open(SAVEOUT, ">&STDOUT"); open(SAVEERR, ">&STDERR"); close(STDIN); open(STDIN, $stdin_file); close(STDOUT); open(STDOUT,'>' . $stdout_file); close(STDERR); open(STDERR,'>' . $stderr_file); select(STDERR); $| = 1; select(STDOUT); $| = 1; eval $perl_script_content; my $result_code = $@;

Works, but now I have to read the files back in to get the results, and files are so passe.

Is there a direct way to redirect what would go to STDOUT and STDERR to variables, for the purpose of catching the output of my eval? I imagine the same could be used to redirect my STDIN from a variable.

I'm using an eval to avoid invoking the shell. I do want the perl code that is to be run to work standalone though, so no rewriting it as a sub.

I've seen working examples using a double fork, but that seems excessive.

Thank you Monks,

Replies are listed 'Best First'.
Re: Reassign STDOUT/STDERR contents to a variable
by almut (Canon) on Sep 25, 2009 at 17:32 UTC
    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.

      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? :)

        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)))

        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)))