for i in $(xrandr); do echo "$i" ; done
for i in "$(xrandr)"; do echo "$i"; done
for i in "$(xrandr)"; do echo $i; done
我明白为什么 1 与 2 不同。但是为什么 3 给出的输出与 2 不同?请也解释一下输出。换行符上的引号如何工作?
答案1
不带引号的变量(如$var
)或命令替换(如$(cmd)
或`cmd`
)是分割+全局类似 Bourne shell 的操作符。
也就是说,它们的内容根据$IFS
特殊变量的当前值进行分割(默认情况下包含空格、制表符和换行符)
然后分裂产生的每个单词都会受到文件名生成(也称为通配或者文件名扩展),也就是说,它们被视为模式并扩展到与该模式匹配的文件列表。
因此for i in $(xrandr)
,在 中$(xrandr)
,由于 不在引号内,因此按空格、制表符和换行符序列进行分割。并且检查该分割产生的每个单词是否匹配文件名(或者如果它们不匹配任何文件则保持原样),并for
循环遍历它们。
在 中for i in "$(xrandr)"
,我们没有使用 split+glob 运算符作为命令替换被引用,所以循环中有一次一xrandr
值:(不带尾随换行符的输出命令替换带子)。
然而,在echo $i
,中$i
再次未被引用,因此 的内容再次$i
被分割并受文件名生成这些作为单独的参数传递给echo
命令(并echo
输出用空格分隔的参数)。
所以吸取的教训是:
- 如果你不想分词或者文件名生成,始终引用变量扩展和命令替换
- 如果你确实想要分词或者文件名生成,将它们不加引号,但进行
$IFS
相应设置和/或启用或禁用文件名生成(如果需要)(set -f
,set +f
)。
通常,在上面的示例中,如果您想循环 的输出中空白分隔的单词列表xrandr
,您需要:
- 保留
$IFS
默认值(或取消设置)以在空白处拆分 - 用于
set -f
禁用文件名生成,除非您确定xrandr
永远不会输出任何*
或?
或[
字符(这是文件名生成模式中使用的通配符)
in
然后在循环部分仅使用 split+glob 运算符(仅保留命令替换或变量扩展不带引号)for
:
set -f; unset -v IFS
for i in $(xrandr); do whatever with "$i"; done
如果你想循环(非空)线对于xrandr
输出,您需要设置$IFS
为换行符:
IFS='
'
答案2
引用的换行符是换行符。因此,echo "$1"
为 echo 提供一个命令行参数,然后直接打印换行符。
不带引号的换行符是空格。因此,echo $1
给 echo 提供了许多命令行参数,它会依次打印它们,并用空格分隔。