15

To escape the string to be used as shell argument we use the function escapeshellarg() in PHP. Does Perl have an equivalent function ?

Zacky112
  • 8,679
  • 9
  • 34
  • 36

4 Answers4

31

String::ShellQuote, but most of the time this is not needed. You simply can avoid invoking the shell by careful programming. For example, system takes a list of arguments instead of a string.

Best practice:

use IPC::System::Simple qw(systemx);
systemx($command, @arguments);

require IPC::System::Simple;
use autodie qw(:all);
system([@allowed_exit_values], $command, @arguments);
daxim
  • 39,270
  • 4
  • 65
  • 132
  • 5
    Yes, just avoid the shell entirely if at all possible. – Chris Johnsen Jul 09 '10 at 11:49
  • 3
    Good job answering an [XY question](http://www.perlmonks.org/index.pl?node_id=542341) with the proper solution. :) – Ether Jul 09 '10 at 15:07
  • Can this help though when you need redirection (so can't use the list form of system() invocation) *and* arguments from variables? e.g. a solution to the following failing attempt to `ls 'f o o'`: `my $foo='f o o'; my $result = \`ls $foo 2>&1\`;` ? – artfulrobot Apr 23 '14 at 11:04
  • Yes --- avoid shell parsing whenever possible. But it isn't always possible. – Sophia_ES May 31 '17 at 23:37
  • Also, `use IPC::System::Simple qw(systemx EXIT_ANY $EXITVAL); systemx(EXIT_ANY, $command, @arguments);` will prevent exceptions and indicate success in `$EXITVAL`. See [`IPC::System::Simple#ADVANCED_SYNOPSIS`](http://search.cpan.org/~pjf/IPC-System-Simple-1.25/lib/IPC/System/Simple.pm#ADVANCED_SYNOPSIS) – sshine Mar 20 '18 at 10:53
3

Perl can match the following stated function:

adds single quotes around a string and quotes/escapes any existing single quotes

http://php.net/manual/en/function.escapeshellarg.php#function.escapeshellarg

like this:

sub php_escapeshellarg { 
    my $str = @_ ? shift : $_;
    $str =~ s/((?:^|[^\\])(?:\\\\)*)'/$1'\\''/g;
    return "'$str'";
}
Axeman
  • 29,660
  • 2
  • 47
  • 102
  • 4
    This does not work. Consider passing the string `foo'bar` to the function. It will produce the following string: `'foo\'bar'`. But there can be no escaped single quotes within single quotes. – josch Apr 23 '16 at 09:54
1

I have to agree with @daxim. But there are some circumstances where you can't use this like passing a command over a socket to a program that isn't perl or doesn't have IPC module available. I also looked at the regexp given by @axeman and that seems a bit complex to me. I could be wrong, but I think you don't need to escape backslashes in single quoted strings for a command. So, you could just do this:

sub escapeshellarg {
    my $arg = shift;

    $arg =~ s/'/'\\''/g;
    return "'" . $arg . "'";
}

If anyone has any reason, why this may be insecure, I would like to know. I have tested it using any kind of trickery I could think of to make the shell execute arbitrary code, without success, which makes me think this regexp is the same as the PHP implementation.

In the PHP implementation, it states that all it does is: adds single quotes around a string and quotes/escapes any existing single quotes.

Which is the only thing this regexp does. Thoughts?

Ray Perea
  • 5,640
  • 1
  • 35
  • 38
  • 2
    I'm sceptical, I would always prefer the cpantesters-proven [String::ShellQuote](http://p3rl.org/String::ShellQuote) that's been deployed in the wild for years and has the bug reports and -fixes to show for it over an SO user's ad-hoc regex. You can compare its code with yours and figure out yourself what you are lacking. – daxim May 28 '12 at 14:46
  • @daxim they're not too easy to compare: http://api.metacpan.org/source/ROSCH/String-ShellQuote-1.04/ShellQuote.pm – dlamblin Oct 16 '12 at 15:17
  • Darn, sorry I accidentally downvoted your answer and now I cannot undo it. :( – josch Apr 23 '16 at 10:03
  • @daxim, it's the other way round. [String::ShellQuote has a number of issues](https://rt.cpan.org/Public/Dist/Display.html?Name=String-ShellQuote) which this code doesn't have. – Stephane Chazelas Oct 25 '16 at 21:43
1

This one works for me in BASH:

sub escape_shell_param($) {
   my ($par) = @_;
   $par =~ s/'/'"'"'/g;  # "escape" all single quotes
   return "'$par'";      # single-quote entire string
 }

It's based on the following statements taken from the BASH man page:

  1. Enclosing characters in single quotes preserves the literal value of each character within the quotes.
  2. A single quote may not occur between single quotes, even when preceded by a backslash.
  3. Enclosing characters in double quotes preserves the literal value of all characters within the quotes, with the exception of $, `, \, and, when history expansion is enabled, !.

From (2.) we know that backslash cannot be used to escape single qoutes, but from (3.) it's implied that double quotes preserve the literal value of (aka escape) single quotes.

Also note that the only purpose of quotes is to change the meaning of the quoted characters (no special string objects or similar things are created) and after expansion all consecutive literal characters are joined together. Then, 'foo''bar' is the same as foobar.

What the function above does is to single-quote the entire string so that no charcter in it has special meaning. However, to allow for the presence of single quote characters, the string is split wherever such characters are found, and the following operations are done: the previous substring is finalized ('), then a double-quoted single quote is introduced ("'") and the next substring is started ('). That's why ' is globally replaced by '"'"'.

For example (pseudocode):

foo'bar => 'foo' + "'" + 'bar'
roy
  • 463
  • 5
  • 10