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

例如:

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

并使用 \转义字符:

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

我显然在做傻事。

如何得到输出 BAR * BAR

141375 次浏览
FOO='BAR * BAR'
echo "$FOO"
echo "$FOO"

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

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

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

在这个示例中,它没有提供多少好处,但是对于更复杂的输出,它可能更有用。

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

简短的回答

正如其他人所说,你应该总是引用变量,以防止奇怪的行为。因此,在其中使用 Echo“ $foo”而不仅仅是 Echo $foo

答案很长

我确实认为这个例子值得进一步解释,因为事情比表面上看起来的要复杂得多。

我知道你为什么会感到困惑,因为在你运行了第一个例子之后,你可能会想到 shell 显然在做:

  1. 参数展开
  2. 文件名扩展

从你的第一个例子开始:

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

After 参数展开等效于:

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 在将其发送到 echo 命令之前删除了它,但是在第二个例子中,“ *”是前一个 Parameter 扩展的结果,所以它没有被删除。结果,echo 接收“ *”并打印出来。

注意第一个示例-“ *”之间的区别不包括在引号删除操作将要删除的字符中。

我希望这能说得通。最后在同一个结论-只是使用引号。我只是想解释一下为什么转义不起作用,如果只使用 Parameter 和 Filename 扩展,那么转义在逻辑上应该起作用。

有关 BASH 扩展的完整解释,请参阅:

Http://www.gnu.org/software/bash/manual/bashref.html#shell-expansions

我会在这条旧线上加点东西。

通常你会用

$ 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

如果您不想为 bash 的奇怪扩展操心,可以这样做

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

这里解释为什么使用 -e 选项的 echo 使生活更容易:

相关引用来自这里的人:

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