Perl Escaping Argument for Bash Execution

Perl escaping argument for bash execution

It's much easier to use single quotes in bash; then the only character you need to worry about is a single quote itself.

($arbitrary_string) =~ s/'/'"'"'/g;

`echo '$arbitrary_string'`

Should I escape shell arguments in Perl?

If you use system $cmd, @args rather than system "$cmd @args" (an array rather than a string), then you do not have to escape the arguments because no shell is invoked (see system). system {$cmd} $cmd, @args will not invoke a shell either even if $cmd contains metacharacters and @args is empty (this is documented as part of exec). If the args are coming from user input (or other untrusted source), you will still want to untaint them. See -T in the perlrun docs, and the perlsec docs.

If you need to read the output or send input to the command, qx and readpipe have no equivalent. Instead, use open my $output, "-|", $cmd, @args or open my $input, "|-", $cmd, @args although this is not portable as it requires a real fork which means Unix only... I think. Maybe it'll work on Windows with its simulated fork. A better option is something like IPC::Run, which will also handle the case of piping commands to other commands, which neither the multi-arg form of system nor the 4 arg form of open will handle.

Perl backticks using bash

Capture::Tiny is a very nice option: as the SYNOPSIS shows, you can do

use Capture::Tiny 'capture';
my ($output, $error_output, $exit_code) = capture {
system(@whatever);
};

as well as using system inside capture_stdout if you want the simpler behavior of backticks.

Plus it's very general-purpose, working on Perl code (even Perl code that does weird stuff) as well as external programs, so it's a good thing to have in your toolbox.

How can I escape double quotes in the system command in this perl oneliner?

I'd like to find out how to escape quotes within the system command in
the following perl oneliner:

$ ls -p | grep -v / | perl -lane 'system("echo \"$_\"");'

without using the \" escape sequence, in the manner shown here: [...]

Those examples are for escaping single quotes, your example uses double quotes \"$_\". In Bash everything within single quote are taken literally (except the single quote itself), the same is not true for that within double quotes. For example a $ within double quotes are not a literal $ but indicates that a variable expansion is to follow.

Now, in your command there are three levels of quote interpretation.

  • First the outer shell (the command line):

    perl -lane 'system("echo \"$_\"");'
  • Then the perl interpreter:

    system("echo \"$_\");
  • Then a second shell (here, Bash is used):

    echo "xxx"

where xxx is the expansion of $_ in the perl script

Your command does not contain any internal single quotes, so there is no need to use the Bash quoting constructs for escaping single quotes, like

It's fine  -> 'It'"'"'s fine'

or

It's fine  -> 'It'\''s fine'

Further, you need to escape the double quote in the perl script

system("echo \"$_\"")

since they are inside double quotes, alternatively you could remove them
since echo does not need double quotes here:

system("echo $_")

would work fine, or you could do as proposed in the comments:

system(qq/echo "$_"/)

or use the LIST form of system with more than one argument

system("/bin/echo", $_)

However, if you wanted to use single quotes, for example:

system('echo ' . $_)

you would need to invoke the shell's single quote escapes:

'system('"'"'echo ''"' . $_)'

or (using double quotes):

"system('echo ' . "'$'"_)"

Escaping whitespace within nested shell/perl scripts

To run it as posted test.sh "myscript.pl -g \"Some Example\" -n 1 -p 45" do this:

#!/bin/bash
eval "$1"

This causes the $1 argument to be parsed by the shell so the individual words will be broken up and the quotes removed.

Or if you want you could remove the quotes and run test.sh myscript.pl -g "Some Example" -n 1 -p 45 if you changed your script to:

#!/bin/bash
"$@"

The "$@" gets replaced by all the arguments $1, $2, etc., as many as were passed in on the command line.

How can I safely pass a filename with spaces to an external command in Perl?

Are you looking for quotemeta?

Returns the value of EXPR with all non-"word" characters backslashed.

Update: As hobbs points out in the comments, quotemeta is not intended for this purpose and upon thinking a little more about it, might have problems with embedded nuls. On the other hand String::ShellQuote croaks upon encountering embedded nulls.

The safest way is to avoid the shell entirely. Using the list form of 'system' can go a long way towards that (I found out to my dismay a few months ago that cmd.exe might still get involved on Windows), I would recommend that.

If you need the output of the command, you are best off (safety-wise) opening a pipe yourself as shown in hobbs' answer

Perl to exec a program with arguments containing @

As an argument to a command in the shell,

-f="<ashish@isthisreal.com>"

causes the the string

-f=<ashish@isthisreal.com>

to be passed to the program. Your program passes

-f="<ashish\@isthisreal.com>"

to the program. The problem isn't the @; the problem is the " and \ you are adding.

my $mf = $msginfo->sender_smtp;
push @gd_args_msg, "-f=$mf"; # Assuming $mf is <ashish@isthisreal.com>

Perl regex directly escaping special characters

Interpolation happens first, then \Q, \U, \u, \L and \l.

That means

"abc\Qdef$ghi!jkl\Emno"

is equivalent to

"abc" . quotemeta("def" . $ghi . "!jkl") . "mno"

So,

s/\Q$~^/Two/    # not ok   quotemeta($~ . "^")
s/\Q$Sub/Two/ # ok
s/\$\~\^/Two/ # ok
s/\$\Q~^/Two/ # ok


Related Topics



Leave a reply



Submit