eg.
例如。
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”?
6 个解决方案
#1
101
Quoting when setting $FOO is not enough. You need to quote the variable reference as well:
设置$ FOO时引用是不够的。您还需要引用变量引用:
me$ FOO="BAR * BAR"
me$ echo "$FOO"
BAR * BAR
#2
80
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显然正在做:
- Parameter expansion
- 参数扩展
- Filename expansion
- 文件名扩展
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 * BAR
into 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.
请注意第一个示例之间的差异 - “*”不包含在将由报价删除删除的字符中。
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
#3
38
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 "$1"
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_opts
because it will be recognized as a single (invalid) option to 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 bash
variable $GLOBIGNORE
to prevent filename expansion altogether if applied to the global pattern, or use the set -f
built-in flag.
因此,如果应用于全局模式,或者使用set -f内置标志,我建议使用bash变量$ GLOBIGNORE来完全阻止文件名扩展。
#!/bin/bash
GLOBIGNORE="*"
curl_opts="-s --noproxy * -O"
curl $curl_opts "$1" ## 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
#4
3
FOO='BAR * BAR'
echo "$FOO"
#5
3
echo "$FOO"
#6
3
It may be worth getting into the habit of using printf
rather then echo
on 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"
#1
101
Quoting when setting $FOO is not enough. You need to quote the variable reference as well:
设置$ FOO时引用是不够的。您还需要引用变量引用:
me$ FOO="BAR * BAR"
me$ echo "$FOO"
BAR * BAR
#2
80
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显然正在做:
- Parameter expansion
- 参数扩展
- Filename expansion
- 文件名扩展
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 * BAR
into 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.
请注意第一个示例之间的差异 - “*”不包含在将由报价删除删除的字符中。
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
#3
38
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 "$1"
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_opts
because it will be recognized as a single (invalid) option to 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 bash
variable $GLOBIGNORE
to prevent filename expansion altogether if applied to the global pattern, or use the set -f
built-in flag.
因此,如果应用于全局模式,或者使用set -f内置标志,我建议使用bash变量$ GLOBIGNORE来完全阻止文件名扩展。
#!/bin/bash
GLOBIGNORE="*"
curl_opts="-s --noproxy * -O"
curl $curl_opts "$1" ## 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
#4
3
FOO='BAR * BAR'
echo "$FOO"
#5
3
echo "$FOO"
#6
3
It may be worth getting into the habit of using printf
rather then echo
on 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"