为什么我们在 Bash 中对美元符号求值加双引号?

为什么我们在 Bash 中对美元符号求值加双引号?

我知道单引号不会评估里面的内容,而双引号会。我经常看到人们引用美元符号的评价。这里有些例子:

  • for i in "${indices[@]}"; do
  • if [ "${a}" == 0 ]; then
  • ffmpeg -framerate 2 -pattern_type glob -i "*.png" -pix_fmt yuv420p output.mp4

如果我们不对美元符号表达式加双引号怎么办? AFAIK,for循环仍然有效。

答案1

你的三个例子并不完全相同。

在最后两个中:

if [ "${a}" == 0 ]; then
ffmpeg -framerate 2 -pattern_type glob -i "*.png" -pix_fmt yuv420p output.mp4

如果$a没有加引号,并且其值包含字符$IFS(默认情况下为空格、制表符和换行符)或通配符,则会导致[接收超过三个参数(除了[]之外),从而导致错误;类似地,如果的值$a是空字符串,则会导致 [接收的参数太少:

$ (a=0; [ $a == 0 ] && echo OK)
OK

(但前提是$IFS当前恰好不包含0

$ (a='foo bar'; [ $a == 0 ] && echo OK)
bash: [: too many arguments

(默认值为$IFS

$ (a=; [ $a == 0 ] && echo OK)
bash: [: ==: unary operator expected

(即使使用空$IFS或 with zsh(否则不会在不带引号的扩展上实现隐式 split+glob 运算符))

$ (a='*'; [ $a == 0 ] && echo OK)
bash: [: too many arguments

(当在至少包含 2 个非隐藏文件的目录中运行时)。

通过引用,没有错误:

$ (a='foo bar'; [ "$a" == 0 ] && echo OK)
$ (a=; [ "$a" == 0 ] && echo OK)

你的第一个例子是不同的。当涉及数组时,双引号内扩展的规则是特殊的;如果a表示一个数组,则:

  • $a是数组的第一个元素(严格来说,${a[0]}即使索引 0 处的元素未定义也是如此);

  • ${a[*]}${a[@]}是数组的元素,另外在$IFS(默认情况下为空格、制表符、换行符)处分割;

  • "${a[@]}"是数组的元素,未在 处分割$IFS

因此,您的循环for i in "${indices[@]}"; do ...实际上并不相同,具体取决于数组的内容。例如:

$ (declare -a a=(a b c); printf '%s\n' $a)
a

$ (declare -a a=(a b c); printf '%s\n' ${a[*]})
a
b
c

$ (declare -a a=(a 'b c'); printf '%s\n' ${a[*]})
a
b
c

$ (declare -a a=(a 'b c'); printf '%s\n' ${a[@]})
a
b
c

$ (declare -a a=(a 'b c'); printf '%s\n' "${a[*]}")
a b c

$ (declare -a a=(a 'b c'); printf '%s\n' "${a[@]}")
a
b c

答案2

长话短说,这样做是为了避免变量中可能存在的特殊字符(如)造成的扩展!。双引号也称为“弱引用”,因为某些字符将被解释(美元符号、反引号和反斜杠)。单引号是“强引用”,不会解释任何内容。

引用bash 文档

3.1.2.3 双引号

将字符括在双引号 ( ) 中会保留引号内所有字符的字面值,但、 、和(启用历史扩展时)"除外。当 shell 处于 POSIX 模式时(请参阅 Bash POSIX 模式),双引号内的 没有特殊含义,即使启用了历史扩展...$backquote\!!

弱引用和强引用的示例:

  • echo "Your PATH is: $PATH"- 将打印Your PATH is:<variable_content>
  • echo 'Your PATH is: $PATH'- 将打印Your PATH is: $PATH

回答您的问题,我们对变量使用双引号以允许$扩展,但避免变量内容进一步扩展。

相关内容:

相关内容