Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I escape an arbitrary string for use as a command line argument in Bash?

Tags:

bash

perl

I have a list of strings and I want to pass those strings as arguments in a single Bash command line call. For simple alphanumeric strings it suffices to just pass them verbatim:

> script.pl foo bar baz yes no
foo
bar
baz
yes
no

I understand that if an argument contains spaces or backslashes or double-quotes, I need to backslash-escape the double-quotes and backslashes, and then double-quote the argument.

> script.pl foo bar baz "\"yes\"\\\"no\""
foo
bar
baz
"yes"\"no"

But when an argument contains an exclamation mark, this happens:

> script.pl !foo
-bash: !foo: event not found

Double quoting doesn't work:

> script.pl "!foo"
-bash: !foo: event not found

Nor does backslash-escaping (notice how the literal backslash is present in the output):

> script.pl "\!foo"
\!foo

I don't know much about Bash yet but I know that there are other special characters which do similar things. What is the general procedure for safely escaping an arbitrary string for use as a command line argument in Bash? Let's assume the string can be of arbitrary length and contain arbitrary combinations of special characters. I would like an escape() subroutine that I can use as below (Perl example):

$cmd = join " ", map { escape($_); } @args;

Here are some more example strings which should be safely escaped by this function (I know some of these look Windows-like, that's deliberate):

yes
no
Hello, world      [string with a comma and space in it]
C:\Program Files\ [path with backslashes and a space in it]
"                 [i.e. a double-quote]
\                 [backslash]
\\                [two backslashes]
\\\               [three backslashes]
\\\\              [four backslashes]
\\\\\             [five backslashes]
"\                [double-quote, backslash]
"\T               [double-quote, backslash, T]
"\\T              [double-quote, backslash, backslash, T]
!1                
!A                
"!\/'"            [double-quote, exclamation, backslash, forward slash, apostrophe, double quote]
"Jeff's!"         [double-quote, J, e, f, f, apostrophe, s, exclamation, double quote]
$PATH             
%PATH%            
&                 
<>|&^             
*@$$A$@#?-_       

EDIT:

Would this do the trick? Escape every unusual character with a backslash, and omit single or double quotes. (Example is in Perl but any language can do this)

sub escape {
    $_[0] =~ s/([^a-zA-Z0-9_])/\\$1/g;
    return $_[0];
}
like image 881
qntm Avatar asked Jun 10 '11 12:06

qntm


People also ask

How do I escape a Bash string?

To escape a string for use as a command line argument in Bash, simply put a backslash in front of every non-alphanumeric character. Do not wrap the string in single quotes or double quotes.

How do you escape a string in shell script?

A non-quoted backslash, \, is used as an escape character in Bash. It preserves the literal value of the next character that follows, with the exception of newline.

How do you escape a variable in Bash?

Escape characters: Bash escape character is defined by non-quoted backslash (\). It preserves the literal value of the character followed by this symbol. Normally, $ symbol is used in bash to represent any defined variable.


4 Answers

If you want to securely quote anything for Bash, you can use its built-in printf %q formatting:

cat strings.txt:

yes no Hello, world C:\Program Files\ " \ \\ \\\ \\\\ \\\\\ "\ "\T "\\T !1 !A "!\/'" "Jeff's!" $PATH %PATH% & <>|&^ *@$$A$@#?-_ 

cat quote.sh:

#!/bin/bash while IFS= read -r string do     printf '%q\n' "$string" done < strings.txt 

./quote.sh:

yes no Hello\,\ world C:\\Program\ Files\\ \" \\ \\\\ \\\\\\ \\\\\\\\ \\\\\\\\\\ \"\\ \"\\T \"\\\\T \!1 \!A \"\!\\/\'\" \"Jeff\'s\!\" \$PATH %PATH% \& \<\>\|\&\^ \*@\$\$A\$@#\?-_ 

These strings can be copied verbatim to for example echo to output the original strings in strings.txt.

like image 135
l0b0 Avatar answered Oct 11 '22 21:10

l0b0


What is the general procedure for safely escaping an arbitrary string for use as a command line argument in Bash?

Replace every occurrence of ' with '\'', then put ' at the beginning and end.

Every character except for a single quote can be used verbatim in a single-quote-delimited string. There's no way to put a single quote inside a single-quote-delimited string, but that's easy enough to work around: end the string ('), then add a single quote by using a backslash to escape it (\'), then begin a new string (').

As far as I know, this will always work, with no exceptions.

like image 43
Tanner Swett Avatar answered Oct 11 '22 21:10

Tanner Swett


You can use single quotes to escape strings for Bash. Note however this does not expand variables within quotes as double quotes do. In your example, the following should work:

script.pl '!foo'

From Perl, this depends on the function you are using to spawn the external process. For example, if you use the system function, you can pass arguments as parameters so there"s no need to escape them. Of course you"d still need to escape quotes for Perl:

system("/usr/bin/rm", "-fr", "/tmp/CGI_test", "/var/tmp/CGI");
like image 43
jjmontes Avatar answered Oct 11 '22 21:10

jjmontes


sub text_to_shell_lit(_) {
   return $_[0] if $_[0] =~ /^[a-zA-Z0-9_\-]+\z/;
   my $s = $_[0];
   $s =~ s/'/'\\''/g;
   return "'$s'";
}

See this earlier post for an example.

like image 38
ikegami Avatar answered Oct 11 '22 22:10

ikegami