如何在bash中转义通配符/星号字符?

时间:2008-09-19 14:02:00

标签: bash shell escaping

例如

me$ FOO="BAR * BAR"
me$ echo $FOO
BAR file1 file2 file3 file4 BAR

并使用“\”转义字符:

me$ FOO="BAR \* BAR"
me$ echo $FOO
BAR \* BAR

我显然做了一些愚蠢的事。

如何获得输出“BAR * BAR”?

7 个答案:

答案 0 :(得分:118)

设置$ FOO时引用是不够的。您还需要引用变量引用:

me$ FOO="BAR * BAR"
me$ echo "$FOO"
BAR * BAR

答案 1 :(得分:91)

简短回答

就像其他人所说的那样 - 你应该总是引用变量来防止奇怪的行为。所以使用 echo“$ foo”代替 echo $ foo

LONG ANSWER

我确实认为这个例子需要进一步解释,因为它的表面上看起来比它看起来更多。

我可以看到你的困惑在哪里,因为在你运行你的第一个例子后,你可能会认为shell显然正在做:

  1. 参数扩展
  2. 文件名扩展
  3. 所以从你的第一个例子开始:

    me$ FOO="BAR * BAR"
    me$ echo $FOO
    

    参数扩展等同于:

    me$ echo BAR * BAR
    

    文件名扩展后相当于:

    me$ echo BAR file1 file2 file3 file4 BAR
    

    如果您只是在命令行中键入echo BAR * BAR,您将看到它们是等效的。

    所以你可能会想到“如果我逃避*,我可以防止文件名扩展”

    从第二个例子开始:

    me$ FOO="BAR \* BAR"
    me$ echo $FOO
    

    参数扩展后应相当于:

    me$ echo BAR \* BAR
    

    文件名扩展后应相当于:

    me$ echo BAR \* BAR
    

    如果您尝试直接在命令行中键入“echo BAR \ * BAR”,它将确实打印“BAR * BAR”,因为转义会阻止文件名扩展。

    那么为什么使用$ foo不起作用?

    这是因为发生了第三次扩张 - 报价清除。从bash手册中删除引用是:

      

    在前面的扩展之后,所有   不带引号的字符出现   没有结果的'\','''和'''   从上面的一个扩展是   除去。

    所以当你直接在命令行输入命令时,转义字符不是先前扩展的结果,所以BASH在将它发送到echo命令之前将其删除,但在第二个例子中,“ *“是之前参数扩展的结果,因此不会被删除。结果,echo收到“\ *”,这就是它打印的内容。

    请注意第一个示例之间的差异 - “*”不会包含在报价删除中删除的字符中。

    我希望这是有道理的。最后得出的结论 - 只是使用引号。我只是想我会解释为什么转义,如果只有参数和文件名扩展在起作用,它在逻辑上应该可以工作,不起作用。

    有关BASH扩展的完整说明,请参阅:

    http://www.gnu.org/software/bash/manual/bashref.html#Shell-Expansions

答案 2 :(得分:46)

我会在这个老线程中加一点。

通常你会使用

$ echo "$FOO"

但是,即使使用这种语法,我也遇到了问题。请考虑以下脚本。

#!/bin/bash
curl_opts="-s --noproxy * -O"
curl $curl_opts "$1"

*需要逐字传递给curl,但会出现同样的问题。上面的示例将不起作用(它将扩展到当前目录中的文件名),\*也不会。您也无法引用$curl_opts,因为它会被识别为curl的单个(无效)选项。

curl: option -s --noproxy * -O: is unknown
curl: try 'curl --help' or 'curl --manual' for more information

因此,如果应用于全局模式,我建议使用bash变量$GLOBIGNORE来完全阻止文件名扩展,或者使用set -f内置标记。

#!/bin/bash
GLOBIGNORE="*"
curl_opts="-s --noproxy * -O"
curl $curl_opts "$1"  ## no filename expansion

应用原始示例:

me$ FOO="BAR * BAR"

me$ echo $FOO
BAR file1 file2 file3 file4 BAR

me$ set -f
me$ echo $FOO
BAR * BAR

me$ set +f
me$ GLOBIGNORE=*
me$ echo $FOO
BAR * BAR

答案 3 :(得分:4)

FOO='BAR * BAR'
echo "$FOO"

答案 4 :(得分:3)

echo "$FOO"

答案 5 :(得分:3)

在命令行中习惯使用printf而不是echo可能是值得的。

在这个例子中,它并没有带来太多好处,但对于更复杂的输出它可能更有用。

FOO="BAR * BAR"
printf %s "$FOO"

答案 6 :(得分:0)

如果您不想打扰bash的怪异扩展,可以这样做

me$ FOO="BAR \x2A BAR"   # 2A is hex code for *
me$ echo -e $FOO
BAR * BAR
me$ 

在这里解释为什么使用echo的-e选项使生活更轻松:

此人的相关报价:

SYNOPSIS
   echo [SHORT-OPTION]... [STRING]...
   echo LONG-OPTION

DESCRIPTION
   Echo the STRING(s) to standard output.

   -n     do not output the trailing newline

   -e     enable interpretation of backslash escapes

   -E     disable interpretation of backslash escapes (default)

   --help display this help and exit

   --version
          output version information and exit

   If -e is in effect, the following sequences are recognized:

   \\     backslash

   ...

   \0NNN  byte with octal value NNN (1 to 3 digits)

   \xHH   byte with hexadecimal value HH (1 to 2 digits)

对于十六进制代码,您可以检查man ascii页面(八进制的第一行,第二十进制,第三十六进制):

   051   41    29    )                           151   105   69    i
   052   42    2A    *                           152   106   6A    j
   053   43    2B    +                           153   107   6B    k