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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/102049/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-09 17:40:56  来源:igfitidea点击:

How do I escape the wildcard/asterisk character in bash?

bashshellescaping

提问by andyuk

For example:

例如:

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

and using the \escape character:

并使用\转义字符:

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

I'm obviously doing something stupid.

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

How do I get the output BAR * BAR?

我如何获得输出BAR * BAR

回答by finnw

Quoting when setting $FOOis not enough. You need to quote the variable reference as well:

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

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

回答by mithu

SHORT ANSWER

简答

Like others have said - you should always quote the variables to prevent strange behaviour. So use echo "$foo"in instead of just echo $foo.

就像其他人所说的那样 - 您应该始终引用变量以防止出现奇怪的行为。所以使用echo "$foo"而不仅仅是echo $foo

LONG ANSWER

长答案

I do think this example warrants further explanation because there is more going on than it might seem on the face of it.

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

I can see where your confusion comes in because after you ran your first example you probably thought to yourself that the shell is obviously doing:

我可以看出您的困惑在哪里,因为在您运行第一个示例之后,您可能认为 shell 显然在做:

  1. Parameter expansion
  2. Filename expansion
  1. 参数扩展
  2. 文件名扩展

So from your first example:

所以从你的第一个例子:

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

After parameter expansion is equivalent to:

参数展开后等价于:

me$ echo BAR * BAR

And after filename expansion is equivalent to:

并且经过文件名扩展后相当于:

me$ echo BAR file1 file2 file3 file4 BAR

And if you just type echo BAR * BARinto the command line you will see that they are equivalent.

如果你只是echo BAR * BAR在命令行中输入,你会看到它们是等价的。

So you probably thought to yourself "if I escape the *, I can prevent the filename expansion"

所以你可能想过“如果我转义 *,我可以防止文件名扩展”

So from your second example:

所以从你的第二个例子:

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

After parameter expansion should be equivalent to:

参数展开后应等价于:

me$ echo BAR \* BAR

And after filename expansion should be equivalent to:

并且在文件名扩展之后应该相当于:

me$ echo BAR \* BAR

And if you try typing "echo BAR \* BAR" directly into the command line it will indeed print "BAR * BAR" because the filename expansion is prevented by the escape.

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

So why did using $foo not work?

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

It's because there is a third expansion that takes place - Quote Removal. From the bash manual quote removal is:

这是因为发生了第三次扩展 - 报价删除。从 bash 手动引用删除是:

After the preceding expansions, all unquoted occurrences of the characters ‘\', ‘'', and ‘"' that did not result from one of the above expansions are removed.

在前面的扩展之后,所有不是由上述扩展之一产生的字符 '\'、''' 和 '"' 的所有未加引号的出现都将被删除。

So what happens is when you type the command directly into the command line, the escape character is not the result of a previous expansion so BASH removes it before sending it to the echo command, but in the 2nd example, the "\*" was the result of a previous Parameter expansion, so it is NOT removed. As a result, echo receives "\*" and that's what it prints.

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

Note the difference between the first example - "*" is not included in the characters that will be removed by Quote Removal.

请注意第一个示例之间的区别 - "*" 不包含在将被 Quote Removal 删除的字符中。

I hope this makes sense. In the end the conclusion in the same - just use quotes. I just thought I'd explain why escaping, which logically should work if only Parameter and Filename expansion are at play, didn't work.

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

For a full explanation of BASH expansions, refer to:

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

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

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

回答by AlexandreH

I'll add a bit to this old thread.

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

Usually you would use

通常你会使用

$ echo "$FOO"

However, I've had problems even with this syntax. Consider the following script.

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

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

The *needs to be passed verbatim to curl, but the same problems will arise. The above example won't work (it will expand to filenames in the current directory) and neither will \*. You also can't quote $curl_optsbecause it will be recognized as a single (invalid) option to curl.

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

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

Therefore I would recommend the use of the bashvariable $GLOBIGNOREto prevent filename expansion altogether if applied to the global pattern, or use the set -fbuilt-in flag.

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

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

Applying to your original example:

适用于您的原始示例:

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

回答by tzot

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

回答by Dave Webb

It may be worth getting into the habit of using printfrather then echoon the command line.

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

In this example it doesn't give much benefit but it can be more useful with more complex output.

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

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

回答by Rafa? Dowgird

echo "$FOO"