perl backticks: use bash instead of sh
Asked Answered
S

7

9

I noticed that when I use backticks in perl the commands are executed using sh, not bash, giving me some problems.

How can I change that behavior so perl will use bash?

PS. The command that I'm trying to run is:

paste filename <(cut -d \" \" -f 2 filename2 | grep -v mean) >> filename3
Savil answered 30/7, 2010 at 17:44 Comment(6)
I'm pretty certain the answer is "you don't."Incipient
why? What's your understatement?Savil
sh is universal, bash isn't.Titicaca
@ether: try perl -wle'print `echo foo; ps --forest`' to see sh.Titicaca
BTW, the command I'm trying to run is: paste filename <(cut -d \" \" -f 2 filename2 | grep -v mean) >> filename3Savil
Why are you even shelling out at all? You can do what you want in pure pure. If you want to run things in the shell, use a shell script.Zwickau
P
8

Try

`bash -c \"your command with args\"`

I am fairly sure the argument of -c is interpreted the way bash interprets its command line. The trick is to protect it from sh - that's what quotes are for.

Plaice answered 30/7, 2010 at 18:16 Comment(8)
Now I'm getting errors like -f 1 /tmp/file | grep -v mean) >> /tmp/file2: -c: line 0: unexpected EOF while looking for matching `)' -f 1 /tmp/file3 | grep -v mean) >> /tmp/file4: -c: line 1: syntax error: unexpected end of fileSavil
May be \ aren't needed in my example. Also, don't use " inside your command - use ' .Plaice
SOLVED! The only thing that worked for me was: `bash -c 'my_command'`Savil
You could also evade quoting problems by using open my $fh, '-|', qw(bash -c), $cmdline which is pretty much guaranteed to work exactly as `$cmdline` -- but the interface is different.Shonda
Those \ are useless, and would make far more sense to use single quotes than double-quotes.Bissau
@Bissau Sure, single quotes could be used and would be better. But what if they are already used inside your command with args ? What if the user needs interpolation inside -c argument ?Plaice
@Arkadiy, Re "But what if they are already used inside", The whole point of using single-quotes is that you have less to escape and that they prevent accidental interpolation, so pointing out that you have to escape single quotes (''\'') is disingenuous.Bissau
@Arkadiy, Re "What if the user needs interpolation inside -c argument ?", That would be a very bad thing to do. Single-quotes actually make it impossible to make this mistake! For example, all three of bash -c "cat -- $file", bash -c "cat -- \"$file\"" and bash -c "cat -- '$file'" are buggy. You should use bash -c 'cat -- "$0"' "$file" or FILE="$file" bash -c 'cat -- "$FILE"'Bissau
T
9

The "system shell" is not generally mutable. See perldoc -f exec:

If there is more than one argument in LIST, or if LIST is an array with more than one value, calls execvp(3) with the arguments in LIST. If there is only one scalar argument or an array with one element in it, the argument is checked for shell metacharacters, and if there are any, the entire argument is passed to the system's command shell for parsing (this is "/bin/sh -c" on Unix platforms, but varies on other platforms).

If you really need bash to perform a particular task, consider calling it explicitly:

my $result = `/usr/bin/bash command arguments`;

or even:

open my $bash_handle, '| /usr/bin/bash' or die "Cannot open bash: $!";
print $bash_handle 'command arguments';

You could also put your bash commands into a .sh file and invoke that directly:

my $result = `/usr/bin/bash script.pl`;
Trinitytrinket answered 30/7, 2010 at 18:0 Comment(2)
I still can't get the expected behavior. The reason I wanted bash is that I run ac command using <(grep ...) and it seems sh does not support it (but bash does). If I run my command as a user, directly from bash it works fine. If I run it from perl in backticks then sh shouts "sh: Syntax error: "(" unexpected", if I run it in backticks preceded by /usr/bin/bash I still get the same thing from sh.Savil
@David: I don't quite understand what command you're trying to run; can you edit that into your question with code formatting?Trinitytrinket
P
8

Try

`bash -c \"your command with args\"`

I am fairly sure the argument of -c is interpreted the way bash interprets its command line. The trick is to protect it from sh - that's what quotes are for.

Plaice answered 30/7, 2010 at 18:16 Comment(8)
Now I'm getting errors like -f 1 /tmp/file | grep -v mean) >> /tmp/file2: -c: line 0: unexpected EOF while looking for matching `)' -f 1 /tmp/file3 | grep -v mean) >> /tmp/file4: -c: line 1: syntax error: unexpected end of fileSavil
May be \ aren't needed in my example. Also, don't use " inside your command - use ' .Plaice
SOLVED! The only thing that worked for me was: `bash -c 'my_command'`Savil
You could also evade quoting problems by using open my $fh, '-|', qw(bash -c), $cmdline which is pretty much guaranteed to work exactly as `$cmdline` -- but the interface is different.Shonda
Those \ are useless, and would make far more sense to use single quotes than double-quotes.Bissau
@Bissau Sure, single quotes could be used and would be better. But what if they are already used inside your command with args ? What if the user needs interpolation inside -c argument ?Plaice
@Arkadiy, Re "But what if they are already used inside", The whole point of using single-quotes is that you have less to escape and that they prevent accidental interpolation, so pointing out that you have to escape single quotes (''\'') is disingenuous.Bissau
@Arkadiy, Re "What if the user needs interpolation inside -c argument ?", That would be a very bad thing to do. Single-quotes actually make it impossible to make this mistake! For example, all three of bash -c "cat -- $file", bash -c "cat -- \"$file\"" and bash -c "cat -- '$file'" are buggy. You should use bash -c 'cat -- "$0"' "$file" or FILE="$file" bash -c 'cat -- "$FILE"'Bissau
I
5

This example works for me:

$ perl -e 'print `/bin/bash -c "echo <(pwd)"`'
/dev/fd/63
Ignite answered 30/7, 2010 at 18:16 Comment(0)
S
1

To deal with running bash and nested quotes, this article provides the best solution: How can I use bash syntax in Perl's system()?

my @args = ( "bash", "-c", "diff <(ls -l) <(ls -al)" );
system(@args);
Slob answered 9/12, 2014 at 20:30 Comment(2)
Doesn't catch the output though, which is the purpose of using the backticks...Hyaluronidase
yeah @Hyaluronidase is right. this solution only works if you don't care about the standard out.Slob
C
0

I thought perl would honor the $SHELL variable, but then it occurred to me that its behavior might actually depend on your system's exec implementation. In mine, it seems that exec

will execute the shell (/bin/sh) with the path of the file as its first argument.

You can always do qw/bash your-command/, no?

Capitol answered 30/7, 2010 at 18:0 Comment(1)
I still get "sh: Syntax error: "(" unexpected". See a comment on the original post for the command I try to run.Savil
N
-1

Create a perl subroutine:

sub bash { return `cat << 'EOF' | /bin/bash\n$_[0]\nEOF\n`; }

And use it like below:

my $bash_cmd = 'paste filename <(cut -d " " -f 2 filename2 | grep -v mean) >> filename3';
print &bash($bash_cmd);

Or use perl here-doc for multi-line commands:

$bash_cmd = <<'EOF';
    for (( i = 0; i < 10; i++ )); do
       echo "${i}"
    done
EOF
print &bash($bash_cmd);
Nemathelminth answered 5/9, 2013 at 1:36 Comment(0)
V
-1

I like to make some function btck (which integrates error checking) and bash_btck (which uses bash):

use Carp;

sub btck ($)
{
    # Like backticks but the error check and chomp() are integrated

    my $cmd = shift;
    my $result = `$cmd`;
    $? == 0 or confess "backtick command '$cmd' returned non-zero";
    chomp($result);
    return $result;
}

sub bash_btck ($)
{
    # Like backticks but use bash and the error check and chomp() are
    # integrated

    my $cmd = shift;
    my $sqpc = $cmd;   # Single-Quote-Protected Command
    $sqpc =~ s/'/'"'"'/g;
    my $bc = "bash -c '$sqpc'";
    return btck($bc);
}

One of the reasons I like to use bash is for safe pipe behavior:

sub safe_btck ($)
{
    return bash_btck('set -o pipefail && '.shift);
}
Viyella answered 7/1, 2023 at 0:55 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.