Beefy Boxes and Bandwidth Generously Provided by pair Networks
go ahead... be a heretic
 
PerlMonks  

running string as perl script and pass into STDIN programatically

by gideondsouza (Pilgrim)
on Feb 17, 2013 at 05:39 UTC ( #1019107=perlquestion: print w/ replies, xml ) Need Help??
gideondsouza has asked for the wisdom of the Perl Monks concerning the following question:

I posted a question here about what I am upto with this.

I have $code and $args, $code is perl code in a string and $args is what I want to pass into STDIN.

From my stackoverflow post and @ikegami's help I have this so far:

//time out is from the Time::Out module. my $ret = timeout 5 => sub { my $r = `perl <some_path>/ExecScript.pl '$code' "$arg" &2>1`; return $r; } ; ########### # ExecScript.pl ###### if($ARGV[0]) { my $code = $ARGV[0]; my $arg = $ARGV[1]; open(my $fh,'|-', 'perl', '-', $arg); print $fh $code; close($fh); }
    Problems:
  1. I can't really pass $arg into STDIN, it just goes in as an argument
  2. I can't fetch STDERR

I looked into IPC::Run but I can't really figure how to use it? The docs don't really have method signatures and their return types, stuff like that.

Please help me out

Comment on running string as perl script and pass into STDIN programatically
Download Code
Re: running string as perl script and pass into STDIN programatically
by BrowserUk (Pope) on Feb 17, 2013 at 06:38 UTC

    Do I have this straight?

    From one running perl program, you want to invoke a second copy of perl running a second, pre-existing perl program, pass it a 3rd perl program, in the form of a single string, that it will then pass to a 3rd copy of perl to run.

    And the problems you are having are:

    1. How to pass a single(?) switch from the first program to the second so that it can give it to the third copy of perl as a command line argument.

      Assuming $arg was 'x', this :open(my $fh,'|-', 'perl', '-', $arg); is equivalent to typing this at the command line:

      perl - x

      Which is obviously wrong. Try: open(my $fh,'|-', 'perl', "-$arg" );

    2. How to capture the STDERR output.

      From which instance of Perl?

      You are invoking the second copy of perl with command line redirection, which means the first program should receive all stdout and stderr output from the second.

      But any output from the third instance of perl will be going to its (virtual) console. Is it the STDERR output from this process you are having trouble capturing?

      Because without trying it out, I could not even guess aa to what would happen to it on my OS; never mind on any other. At that point you have a hierarchy of processes like this:

      <someshell> +-- perl +-- <someshell> (because of the command line redirection used +in backticks in the first program.) +-- perl +-- perl

      And you are hoping the command line redirection in second level of shell, will capture both stdout & stderr output from the third level of perl.

    One question: why? (Including, but not limited to:why not just embed the 10 lines of code in second perl program in the first? Wrapped over as a function if necessary.)

    Another question: Could you post an example of the program that is being fed to the 3rd level perl?

    Update:Another question: Why? :)


    With the rise and rise of 'Social' network sites: 'Computers are making people easier to use everyday'
    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.

      So I'll just start from the use case :)

      I'm developing a little pet project project here : www.tryperl.com

      I'm a linux/perl beginner (touched it for the first time 3 months ago) but I want to really learn it well, so this is my learning project

      My problem:
      Given C (=perl code in a string) and A(=argument) I want to evaluate C and just return STDOUT and STDERR (the output of the program, or an error if it occurred). I also want to do this within a timeout.

      So : my ($stdout, $err) = eval_perl($program, $args); What would be the simplest way of writing eval_perl with good performance?

      I did just have a loong discussion on irc #perl and plan on building better security processes later on, but I don't know enough right now. I run the code in a EC2 vm now so I'm pretty safe. So I just want to stick to the above use case.

      I also have a list of things I've tried and my issues here on stackoverflow. No answers yet :( I tried capture::tiny and also App:EvalServer I'm told executes one liners, I want to exec the whole some_script.pl program at once.

      I sort of have an approach, this is how I plan do things, do you see issues, (performance etc?) or any other flaws?

      my $fh = File::Temp->new(); my $fname = $fh->filename; print $fh $code; close $fh; my $retVal = "nothing"; $retVal = timeout 5 => sub { my $r = `perl $fname $arg &2>1`; return $r; }; if ($@){ return $@; } return $retVal;

      My issue is that if $code has an error, that error is printed into the dancer bash console, I want to capture it instead.

      Update

      I also tried using capture_exec from IO::CaptureOutput, but this time, I get the STDERR but STDOUT is instead printed onto the bash command line, if there was a success :( *sigh*.

      I did ($stdout, $stderr, $success, $ex) = capture_exec('perl', "$fname","$arg");

Re: running string as perl script and pass into STDIN programatically
by tmharish (Friar) on Feb 17, 2013 at 17:08 UTC

    This might help, am starting with the output followed by the code:

    Output:

    #################### CODE: #################### print "Hello World!\n" #################### ERRORS #################### WARNING: It is strongly suggested that you use strict, warnings and di +agnostics #################### OUTPUT #################### Hello World! #################### CODE: #################### use strict ; use warnings ; print "Hello World!\n"; #################### ERRORS #################### #################### OUTPUT #################### Hello World! #################### CODE: #################### use strict ; use warnings ; prints "Hello World!\n"; #################### ERRORS #################### String found where operator expected at input_script1361120797.pl line + 6, near "prints "Hello World!\n"" (Do you need to predeclare prints?) syntax error at input_script1361120797.pl line 6, near "prints "Hello +World!\n"" Execution of input_script1361120797.pl aborted due to compilation erro +rs. #################### OUTPUT #################### #################### CODE: #################### `rm` #################### ERRORS #################### Nice Try! #################### OUTPUT #################### #################### CODE: #################### system( "rm" ) ; #################### ERRORS #################### Nice Try! #################### OUTPUT #################### #################### CODE: #################### use strict ; use warnings ; my @arr = ( 0 .. 10 ) ; foreach my $elem ( @arr ) { print $elem . "\n"; } #################### ERRORS #################### #################### OUTPUT #################### 0 1 2 3 4 5 6 7 8 9 10

    Program

    use strict ; use warnings ; my @code = ( q{ print "Hello World!\n" } , q{ use strict ; use warnings ; print "Hello World!\n"; } , q{ use strict ; use warnings ; prints "Hello World!\n"; } , q{ `rm` } , q{ system( "rm" ) ; } , q{ use strict ; use warnings ; my @arr = ( 0 .. 10 ) ; foreach my $elem ( @arr ) { print $elem . "\n"; } } ) ; foreach my $code ( @code ) { my ( $errors, $output ) = evaluate_perl_program( $code ) ; print ' #################### CODE: #################### ' . $code . ' #################### ERRORS #################### ' . $errors . ' #################### OUTPUT #################### ' . $output . "\n"; } exit() ; sub evaluate_perl_program { my $code = shift ; my $errors = '' ; my $output = '' ; if( my $errors = _code_not_secure( $code ) ) { return ( $errors, '' ) ; } $code = '#! ... perl -T ' . "\n" . $code ; # Ensure +taint if( my $basics_not_in_place = _check_for_code_basics( $code ) ) { $errors .= 'WARNING: It is strongly suggested that you use strict, warnin +gs and diagnostics' . "\n" ; } my $exec_file_location = '' ; # Insert +location of Temp Dir here. my $rand_variable = time ; # Set to +time so you can match to apache logs, use rand() if preferred. my $exec_file_name = 'input_script' . time . '.pl' ; open( my $exec_file_handle, ">", $exec_file_location . $exec_file_ +name ) or die( "Unable to open $exec_file_location$exec_file_name" ) +; print $exec_file_handle $code ; close( $exec_file_handle ) ; `perl -T $exec_file_location$exec_file_name > out.$rand_variable 2 +> err.$rand_variable` ; ## Add full path to perl here. open( my $script_outfile_handle, $exec_file_location . 'out.' . $r +and_variable ) or die ( "Failed to open $exec_file_location" . 'out.' + . "$rand_variable" ) ; my @output = <$script_outfile_handle>; $output = join( '', @output ) ; open( my $script_errfile_handle, $exec_file_location . 'err.' . $r +and_variable ) or die ( "Failed to open $exec_file_location" . 'err.' + . "$rand_variable" ) ; my @errors = <$script_errfile_handle>; $errors .= join( '', @errors ) ; return ( $errors, $output ) ; } sub _code_not_secure { my $code = shift ; if( $code =~ /^#\!.*?/ ) { return 'Do not include perl location in your script'; } if( ( $code =~ /system\s*?\(.*?rm/ ) or ( $code =~ /`.*?rm`.*?/ ) +) { return 'Nice Try!'; } if( ( $code =~ /system\s*?\(/ ) or ( $code =~ /`/ ) ) { return 'System calls not allowed in script'; } return 0 ; } sub _check_for_code_basics { my $code = shift ; return 1 unless( ( $code =~ /use\s+strict/ ) and ( $code =~ /use\s+warnings/ ) ); return 0; }
Re: running string as perl script and pass into STDIN programatically
by aitap (Deacon) on Feb 17, 2013 at 18:43 UTC

    I think that this will run your Perl code with STDIN fed and STDOUT/STDERR catched properly:

    use IPC::Run 'run'; my ($stdout, $stderr); run [ qw/perl -e/, $code ], '<', \$args, '>>', \$stdout, '2>>', \$stde +rr;

    Example run:

    $ perl my $code = q[use feature 'say'; say for 1..10; warn 'zzz'; $z = <STDIN +>; $z=~y/a-z/A-Z/; say $z]; my $args = q[this is STDIN, why not?]; use IPC::Run 'run'; my ($stdout, $stderr); run [ qw/perl -e/, $code ], '<', \$args, '>>', \$stdout, '2>>', \$stde +rr; print "STDOUT: $stdout\n"; print "STDERR: $stderr\n"; __END__ STDOUT: 1 2 3 4 5 6 7 8 9 10 THIS IS STDIN, WHY NOT? STDERR: zzz at -e line 1.

    __DATA__ will be broken unless you use real files instead of -e 'code' above. Feeding the code to STDIN (with "\n__END__\n" between the code and actual STDIN) may also work, but can break in strange ways with syntax errors in code.

    Sorry if my advice was wrong.

      Thank you VERY much both of you for your answers :D

      Its super late at night, I gave the code a go and it works. I will try with my app immediately tomorrow.

      Quick question I was planning to use temp files, but should use File::Tempdir for the temporary directory? How should I best take care of all those files generated?

        AFAIU, both File::Temp and File::Tempdir destroy the apporpriate files/directories when their object comes out of scope (e.g. at the end of {} block where it was declared using my).
        Sorry if my advice was wrong.

Log In?
Username:
Password:

What's my password?
Create A New User
Node Status?
node history
Node Type: perlquestion [id://1019107]
Approved by BrowserUk
help
Chatterbox?
and the web crawler heard nothing...

How do I use this? | Other CB clients
Other Users?
Others making s'mores by the fire in the courtyard of the Monastery: (9)
As of 2014-10-25 00:17 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    For retirement, I am banking on:










    Results (138 votes), past polls