154

例如:

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

并使用\转义字符:

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

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

我如何获得输出BAR * BAR

4

7 回答 7

162

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

me$ FOO="BAR * BAR"
me$ echo "$FOO"
BAR * BAR
于 2008-09-19T14:06:06.723 回答
114

简短的回答

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

长答案

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

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

  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 收到“\*”,这就是它打印的内容。

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

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

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

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

于 2008-09-19T17:46:31.407 回答
61

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

通常你会使用

$ 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变量来完全防止文件名扩展,或者使用内置标志。$GLOBIGNOREset -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
于 2012-11-20T23:43:47.277 回答
6
FOO='BAR * BAR'
echo "$FOO"
于 2008-09-19T14:05:55.943 回答
3
echo "$FOO"
于 2008-09-19T14:05:56.083 回答
3

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

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

FOO="BAR * BAR"
printf %s "$FOO"
于 2008-09-19T14:25:56.443 回答
2

如果您不想为 bash 的奇怪扩展而烦恼,您可以这样做

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

这里解释为什么使用 echo 的 -e 选项让生活更轻松:

man here的相关引用:

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
于 2020-12-05T20:55:12.297 回答