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


136

例如:

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

并使用\转义符:

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

我显然在做一些愚蠢的事情。

如何获得输出BAR * BAR

Answers:


139

设置$FOO不足时报价。您还需要引用变量引用:

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

8
这是神秘的,为什么呢?到底是怎么回事?
tofutim

因为变量扩展
丹尼尔(Daniel)

103

短答案

就像其他人所说的那样-您应始终引用变量以防止出现奇怪的行为。因此,使用echo“ $ foo”代替echo $ foo

长答案

我确实认为此示例值得进一步解释,因为正在进行的事情比表面上看起来要多。

我可以看到您困惑的地方,因为在运行第一个示例后,您可能会以为自己外壳显然在做:

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

因此,从您的第一个示例:

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


1
好答案!现在我不觉得我问了一个愚蠢的问题。:-)
andyuk

1
是否存在一些逃避特殊字符的工具?
吉加尔·乔希

“您应该始终引用变量以防止出现奇怪的行为” –当您想将它们用作字符串时
Angelo 2015年


尽管上面@finnw的答案确实可以直接回答问题,但该答案更能解释其原因,这有助于推断我们自己的场景中的用法。这是我们需要更多了解的答案。
Nicolas Coombs

54

我将在此旧线程中添加一些内容。

通常你会用

$ 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

4
很好的解释,谢谢!我的用例是SELECT * FROM etc.,这是唯一可行的方法。
knutole

1
感谢您介绍set -f解决方案!
hachre

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

此方法有效,但不必将第一行的单引号更改为双引号。
finnw

1
不,不是,但是当您要包含特殊的shell字符并且您不想进行任何替换时,最好使用单引号的习惯。
tzot


3

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

在此示例中,它并没有带来太多好处,但是对于更复杂的输出,它可能会更有用。

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

为什么在地狱?printf是一个独立的过程(至少不是bash中的内置程序),并且您演示的printf的使用没有对echo的好处。
ddaa

2
printf的内置在bash,我做我的回答“在这个例子中说,它并没有给多少好处,但也可以是更复杂的输出更加有用。
戴夫·韦伯
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.