如何在Bash中转义任意字符串以用作命令行参数?

时间:2011-06-10 12:31:04

标签: perl bash

我有一个字符串列表,我想在单个Bash命令行调用中将这些字符串作为参数传递。对于简单的字母数字字符串,只需逐字传递它们就足够了:

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

我理解如果一个参数包含空格或反斜杠或双引号,我需要反斜杠 - 转义双引号和反斜杠,然后双引号参数。

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

但是当一个参数包含感叹号时,会发生这种情况:

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

双引号不起作用:

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

也没有反斜杠转义(注意输出中字面反斜杠的含义):

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

我对Bash了解不多,但我知道还有其他特殊字符可以做类似的事情。 在Bash中安全地转义任意字符串以用作命令行参数的一般过程是什么?假设字符串可以是任意长度并包含特殊字符的任意组合。我想要一个escape()子程序,我可以使用如下(Perl示例):

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

以下是一些应该由此函数安全转义的示例字符串(我知道其中一些看起来像Windows一样,这是故意的):

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$@#?-_       

编辑:

这可以解决这个问题吗?使用反斜杠逃避每个不寻常的角色,并省略单引号或双引号。 (例子是Perl,但任何语言都可以这样做)

sub escape {
    $_[0] =~ s/([^a-zA-Z0-9_])/\\$1/g;
    return $_[0];
}

8 个答案:

答案 0 :(得分:19)

如果您想为Bash安全地引用任何,您可以使用其内置的printf %q格式:

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 -d $'\n'
do
    printf %q "$REPLY"
    printf '\n'
done < strings.txt

./quote.sh

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

这些字符串可以逐字复制到例如echo,以输出strings.txt中的原始字符串。

答案 1 :(得分:8)

  

在Bash中安全转义任意字符串以用作命令行参数的一般过程是什么?

将所有'替换为'\'',然后将'放在开头和结尾。

除单引号外的每个字符都可以在单引号分隔的字符串中逐字使用。没有办法在单引号分隔的字符串中放入单引号,但这很容易解决:结束字符串('),然后添加单引号使用反斜杠转义它(\'),然后开始一个新的字符串(')。

据我所知,这将永远有效,没有例外。

答案 2 :(得分:1)

您可以使用单引号来转义Bash的字符串。但请注意,这不会在引号内扩展变量,因为双引号可以。在您的示例中,以下内容应该有效:

script.pl '!foo'

从Perl,这取决于您用于生成外部进程的函数。例如,如果使用system函数,则可以将参数作为参数传递,这样就不需要对它们进行转义。当然,你仍然需要为Perl转义引号:

system("/usr/bin/rm", "-fr", "/tmp/CGI_test", "/var/tmp/CGI");

答案 3 :(得分:1)

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

请参阅前面的post以获取示例。

答案 4 :(得分:1)

每当您看到没有获得所需的输出时,请使用以下方法:

"""\special character"""

特殊字符可能包含! " * ^ % $ # @ ....

例如,如果要创建一个生成另一个bash文件的bash,其中有一个字符串,并且您想为其分配一个值,则可以使用以下示例场景:

Area="(1250,600),(1400,750)"
printf "SubArea="""\""""${Area}"""\""""\n" > test.sh
printf "echo """\$"""{SubArea}" >> test.sh

然后test.sh文件将包含以下代码:

SubArea="(1250,600),(1400,750)"
echo ${SubArea}

提醒您使用换行符\n,我们应该使用printf

答案 5 :(得分:0)

Bash仅在交互模式下解释感叹号。

您可以通过执行以下操作来阻止此操作:

set +o histexpand

在双引号内你必须转义美元符号,双引号,反斜杠,我会说这就是全部。

答案 6 :(得分:0)

这不是一个完整的答案,但我发现有时候通过连接它们来组合单个字符串的两种类型的引用很有用,例如echo "$HOME"'/foo!?.*'

答案 7 :(得分:0)

FWIW,我编写了此函数,该函数使用不同的凭据来调用一组参数。 su命令需要序列化所有参数,这需要对所有参数进行转义,这与我上面建议的printf惯用语一样。

$ escape_args_then_call_as myname whoami

escape_args_then_call_as() {
    local user=$1
    shift

    local -a args
    for i in "$@"; do
        args+=( $(printf %q "${i}") )
    done

    sudo su "${user}" -c "${args[*]}"
}