【发布时间】:2008-09-19 14:01:59
【问题描述】:
例如:
me$ FOO="BAR * BAR"
me$ echo $FOO
BAR file1 file2 file3 file4 BAR
并使用\ 转义字符:
me$ FOO="BAR \* BAR"
me$ echo $FOO
BAR \* BAR
我显然在做一些愚蠢的事情。
如何获得输出BAR * BAR?
【问题讨论】:
例如:
me$ FOO="BAR * BAR"
me$ echo $FOO
BAR file1 file2 file3 file4 BAR
并使用\ 转义字符:
me$ FOO="BAR \* BAR"
me$ echo $FOO
BAR \* BAR
我显然在做一些愚蠢的事情。
如何获得输出BAR * BAR?
【问题讨论】:
设置$FOO 时引用是不够的。您还需要引用变量引用:
me$ FOO="BAR * BAR"
me$ echo "$FOO"
BAR * BAR
【讨论】:
$FOO var 扩展,它也是特殊的字符* 扩展(请参阅tldp.org/LDP/abs/html/special-chars.html)或阅读下面的stackoverflow.com/a/104023/207353 以充分了解细微差别
简短回答
就像其他人所说的那样 - 您应该始终引用变量以防止出现奇怪的行为。所以使用 echo "$foo" 而不是 echo $foo。
长答案
我确实认为这个例子值得进一步解释,因为实际情况比表面上看起来要多。
我可以看出您的困惑来自哪里,因为在您运行第一个示例后,您可能会认为 shell 显然在做:
所以从你的第一个例子开始:
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 接收到 "\*" 并打印出来。
请注意第一个示例之间的区别 - “*”不包含在将被 Quote Removal 删除的字符中。
我希望这是有道理的。最后得出相同的结论 - 只需使用引号。我只是想解释一下为什么转义(如果只有参数和文件名扩展在起作用,从逻辑上讲应该起作用)不起作用。
有关 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
【讨论】:
SELECT * FROM etc.,这是唯一可行的方法。
FOO='BAR * BAR'
echo "$FOO"
【讨论】:
echo "$FOO"
【讨论】:
养成在命令行上使用printf 而不是echo 的习惯可能是值得的。
在此示例中,它并没有带来太多好处,但对于更复杂的输出,它可能会更有用。
FOO="BAR * BAR"
printf %s "$FOO"
【讨论】:
如果您不想为 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
【讨论】: