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

Hi,

I want to run an entire shell script inside a perl script. Below is my code.
#!/usr/bin/perl use strict; use warnings; my $a = do { local $/; <DATA> }; system($a, "option_1"); __DATA__ #!/bin/sh echo "hello" echo $1 echo "hi"

Basically, I want to run the shell script and supply it with arguments. When I run this, it will say something "Can't exec ... No such file or directory...". That is because of the presence of "$1" in the bash script.

But when I run change the system($a, "option_1") to just system("$a option_1"), then it would complain that option_1 command was not found which means that the shell is trying to execute another command.

Also, if I change the system($a, "option_1") to just system("$a"), then it would run fine except that the line echo $1 in the bash script will not give any output. This indicates to me that it is actually possible to run an entire bash script inside perl.

Any ideas on how to resolve running an entire bash script inside perl while supplying some arguments in the bash script?

Of course, I could just save the bash script in a separate file and simply call system against it with arguments and it will be fine. I'm just thinking if there is a way to do what I want.

Thanks in adance.

Replies are listed 'Best First'.
Re: Running Entire Bash Script Inside Perl
by ikegami (Patriarch) on Jun 19, 2009 at 23:20 UTC

    When I run this, it will say something "Can't exec ... No such file or directory...". That is because of the presence of "$1" in the bash script.

    No. The first argument of (a multi-argument call to) system is the name of a command to execute. You're passing the entire script as the name of the command. You could run sh and pass the script to sh via the -c argument.

    #!/usr/bin/perl use strict; use warnings; my $sh_script = do { local $/; <DATA> }; my @args = ( 'option1' ); system(sh => (-c => $sh_script, '--', @args) ) == 0 or die "$?/$!\n"; __DATA__ #!/bin/sh echo "hello" echo $1 echo "hi"

    Another option is to send the script to sh's stdin:

    ... open(my $sh_fh, '|-', sh => ('-s', '--', @args) ) or die "$?/$!\n"; print $sh_fh $sh_script; close($sh_fh); ...

    The -- denotes the end of arguments destined for sh and the start arguments destined for the script.

    Update: Added alternate method.

Re: Running Entire Bash Script Inside Perl
by graff (Chancellor) on Jun 19, 2009 at 23:30 UTC
    It might not be directly relevant to your question (I'm not sure), but check out this old node of mine: shloop -- execute shell command on a list. If nothing else, it might give you some ideas about how to do whatever it is you actually want to do.

    As for what you've posted, if you put the content of your __DATA__ block into an disk file, e.g. call it "foo.sh", then do system( "foo.sh" ) or maybe system( "/bin/sh", "foo.sh" ) then the subshell run by the system call would read the shell script from that file, and run it.

    If the idea is to use perl to change some string in the shell script and then run it, then the trick I used in shloop might help you: open up a pipeline file handle that runs a shell, and then print shell commands to it:

    open( SH, "|-", "/bin/sh" ) or die "can't launch a shell: $!\n"; my $param = "whatever"; print SH "echo hello\n"; print SH "echo $param\n"; print SH "echo hi\n"; close SH;

      That design requires advance knowledge of the script (to change $1 to the parameter). I don't know if that's a problem.

      The implementation fails if $param contains two spaces in a row, a quote, etc due to improper encoding of strings. Fix:

      sub to_sh_lit { my ($s) = @_; die if !utf8::downgrade($s, 1); # Expecting bytes. die if $s =~ /\x00/; # NUL can't be passed. $s =~ s/('+)/'"$1"'/g; return "'$s'"; } my $lit_param = to_sh_lit($param); print SH "echo $lit_param\n";

      Update: Added code.

        That design requires advance knowledge of the script (to change $1 to the parameter).

        Well, there's no telling from the OP (as originally seen) how much is known in advance. Whatever the requirements are for the task, this approach could be adapted to use the available knowledge as needed, and work as well as other approaches.

        The implementation fails if $param contains two spaces in a row, a quote, etc.

        Sure. When printing commands to a shell process, there are lots of ways to go wrong and fail (and/or cause all sorts of mayhem and damage, depending on what sorts of mistakes are made and how various permissions play out when the perl script executes). This approach demands respect and caution; anyone who isn't sure whether they might get it wrong probably shouldn't use it.

Re: Running Entire Bash Script Inside Perl
by cdarke (Prior) on Jun 20, 2009 at 06:08 UTC
    You might like to consider if you really need Bash at all. Personally I can't think of a single thing that Bash can do which Perl cannot - apart from being a command oriented interpretter.

    If you need help converting Bash statements to Perl then you can ask here. Also see 627015 and App::sh2p if you are really desperate.
      A very valid point indeed! ;)
      Respectfully, Brother Hippsta
Re: Running Entire Bash Script Inside Perl
by Anonymous Monk on Jun 19, 2009 at 23:18 UTC
    Create a temporary file, and run that using system, or use pipe open (see perlopentut, perlipc), untested example
    open SH, '|/bin/sh --some-switch-sh-understands--important' or die $!; print SH <DATA>; close SH;
      That doesn't address passing an argument to the script.
        You're correct , the 2nd part doesn't, but first part does :)
Re: Running Entire Bash Script Inside Perl
by hippsta (Acolyte) on Jun 20, 2009 at 02:27 UTC
    Hi There, :)

    Why not do it this way?

    #!/usr/bin/perl

    my $script =<<EOT;
    #!/bin/sh
    echo "hello"
    echo \$1
    echo "hi"
    EOT

    system("echo \"$script\" > script.sh");
    system("./script.sh", "world?");

    Maybe that's too bashy to be considered perl? :D

    Respectfully,

    Brother Hippsta
Re: Running Entire Bash Script Inside Perl
by perliff (Monk) on Jun 20, 2009 at 10:35 UTC
    Its true that perl can do (nearly?) everything bash can do, but a mix of both has been quite productive for me, ( if you are not worried about portability). The method suggested by brother hippsta is also one of my favorites. for example, if I want to create an R script for plotting data, or a gnuplot script, I prefer to create it in this way so that I have full control over all the variables. So if you have a script that has to be run with several different values and several times, and use other programs available on the command line, this is one of the easiest ways to go.

    perliff

    ----------------------

    -with perl on my side

    "If you look at the code too long, the code also looks back at you"

Re: Running Entire Bash Script Inside Perl
by bichonfrise74 (Vicar) on Jun 20, 2009 at 16:24 UTC
    Thanks for all your suggestions.

    The reason why I wanted to 'mix' both Bash and Perl is because I'm using bash to create an expect script. Although I can download the Expect module, I thought it would be easier to just use the native expect command.