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

Sib*_*ing 2 shell bash quoting

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

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

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

dha*_*hag 5

你的三个例子并不完全一样。

在最后两个:

if [ "${a}" == 0 ]; then
ffmpeg -framerate 2 -pattern_type glob -i "*.png" -pix_fmt yuv420p output.mp4
Run Code Online (Sandbox Code Playgroud)

如果$a没有被引用,并且它的值包含字符$IFS(默认为空格、制表符和换行符)或通配符,这将导致[接收三个以上的参数(除了[]),从而导致错误;同样,如果 的值为$a空字符串,这将导致 [接收的参数太少:

$ (a=0; [ $a == 0 ] && echo OK)
OK
Run Code Online (Sandbox Code Playgroud)

(但前提是$IFS当前碰巧不包含0

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

(默认值为$IFS

$ (a=; [ $a == 0 ] && echo OK)
bash: [: ==: unary operator expected
Run Code Online (Sandbox Code Playgroud)

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

$ (a='*'; [ $a == 0 ] && echo OK)
bash: [: too many arguments
Run Code Online (Sandbox Code Playgroud)

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

引用,没有错误:

$ (a='foo bar'; [ "$a" == 0 ] && echo OK)
$ (a=; [ "$a" == 0 ] && echo OK)
Run Code Online (Sandbox Code Playgroud)

你的第一个例子是不同的。当涉及到数组时,关于双引号内扩展的规则是特殊的;如果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
Run Code Online (Sandbox Code Playgroud)