带引号和不带引号的字符串扩展


11
  1. for i in $(xrandr); do echo "$i" ; done
  2. for i in "$(xrandr)"; do echo "$i"; done
  3. for i in "$(xrandr)"; do echo $i; done

我理解为什么1与2不同。但是为什么3与2给出不同的输出?请也解释输出。报价如何在换行符上工作?



Answers:


19

在类似Bourne的shell中,未加引号的变量(如$var)或命令替换(如$(cmd)`cmd`)是split + glob运算符。

也就是说,它们的内容根据$IFS特殊变量的当前值(默认情况下包含空格,制表符和换行符)进行拆分

然后,由该拆分产生的每个单词都将受文件名生成(也称为globbing文件名扩展)的影响,也就是说,它们被视为模式,并被扩展到与该模式匹配的文件列表。

因此for i in $(xrandr),在中$(xrandr),由于不在引号内,所以按空格,制表符和换行符的序列进行分割。然后检查由该拆分产生的每个单词是否匹配文件名(如果它们不匹配任何文件,则保留原样),然后for遍历它们。

在中for i in "$(xrandr)",我们没有使用split + glob运算符,因为命令替换被加了引号,因此循环在一个值上进行了一次遍历:(的输出xrandr(不带命令替换的尾随换行符)。

但是echo $i$i中的再次被取消引用,因此再次$i拆分的内容并生成文件名,然后将其作为单独的参数传递给echo命令(并echo以空格分隔输出其参数)。

因此吸取了教训:

  • 如果您不希望拆分单词生成文件名,请始终引用变量扩展和命令替换
  • 如果您确实希望拆分单词生成文件名,请不要使用引号,而是进行相应设置$IFS和/或根据需要启用或禁用文件名生成(set -fset +f)。

通常,在上面的示例中,如果要遍历的输出中空白的单词分隔列表,则xrandr需要:

  • 保留$IFS其默认值(或取消设置)以分割成空白
  • 使用set -f要禁用文件名代,除非你确信xrandr从不输出任何*?[字符(这是通配符文件名生成模式中使用)

然后infor循环的一部分中仅使用split + glob运算符(仅保留命令替换或变量扩展未引用):

set -f; unset -v IFS
for i in $(xrandr); do whatever with "$i"; done

如果要循环输出的(非空),则xrandr需要设置$IFS为换行符:

IFS='
'

4

带引号的换行符是换行符。因此echo "$1"给echo一个命令行参数,然后直接打印换行符。

未引用的换行符是空格。因此,echo $1给了许多命令行参数以回显,这将它们一个接一个地打印出来,并用空格隔开。


没有引号的换行符是空格,这有点太捷径了,以至于不能真正用来解释IMO的行为。
斯特凡Chazelas
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.