为什么
for i in {1..5}; do x="${i}" echo "$x"; done
不输出
1
2
3
4
5
?
这样做的正确方法是什么?
(已测试for i in {1..5}; do x=$(i) echo "$x"; done
-bash: i: 未找到命令
和别的)
答案1
按要求回答您的问题
为什么不
for i in {1..5}; do x="${i}" echo "$x"; done
输出1
、、、、?2
3
4
5
原因与执行期间评估操作的顺序有关。看看这个命令
x="${i}" echo "$x"
这是做什么的
- 用变量的值替换变量
- 为变量分配临时值
x
- 执行命令
所以你得到
x=1 echo ""
(或x=2 echo ""
,等等)- 在此命令的持续时间内,
x
设置为值1
- 执行命令:
echo ""
您可能认为该命令是两条指令:为 赋值x
,输出其值。但在 shell 语法中,您编写的代码是完全合法的,因此 shell 会毫无异议地执行它。
答案2
您要求回答两个问题:
您要求解释为什么当前代码没有产生预期的输出。
您询问了编写代码的正确方法,以便它确实产生预期的输出。
查看您的代码,我可以看到两种可能的解释来解释为什么您以这种方式编写代码:
for循环语法
在第一种情况下,我会说你在变量赋值后缺少一个分号。如果您想将 for 循环写在一行上,那么您需要在循环体中的每个命令后面放置一个分号。试试这个:
for i in {1..5}; do x="${i}"; echo "$x"; done
另一种选择是使用多行语法编写 for 循环,并用换行符代替分号:
for i in {1..5}
do
x="${i}"
echo "${x}"
done
您还可以混合搭配分号和换行符,例如:
for i in {1..5}; do
x="${i}"; echo "${x}"
done
评估简单命令
在第二种情况下,我想说您可能假设命令序言中的变量赋值(即赋值x="$i"
)发生在命令主体中的变量扩展(即${x}
in的扩展echo "${x}"
)之前。但事实并非如此。为了验证这一点,我们可以参考页面简单的命令扩展在 Bash 手册中或关于简单命令的小节在里面POSIX规范。这两篇参考文献都包含以下段落:
“简单命令”是一系列可选的变量分配和重定向,可以任意顺序,可选地后跟单词和重定向,并由控制运算符终止。
当需要执行给定的简单命令时(即,当任何条件结构,例如 AND-OR 列表或案件语句没有绕过简单命令),以下扩展、赋值和重定向都应从命令文本的开头到结尾执行:
根据以下规则被识别为变量赋值或重定向的单词Shell 语法规则保存以供步骤 3 和 4 中的处理。
非变量赋值或重定向的词应扩展。如果扩展后仍有任何字段,则第一个字段应被视为命令名称,其余字段是命令的参数。
重定向应按中所述执行重定向。
每个变量赋值都应在赋值之前进行扩展,以进行波形符扩展、参数扩展、命令替换、算术扩展和引号删除。
请注意,步骤 2 是命令中发生变量扩展的地方,但步骤 1 告诉我们变量赋值将保存到步骤 3 和 4。因此,表达式echo "${x}"
将扩展至echo ""
赋值x="${i}"
发生之前。这解释了为什么您得到空输出。
有关此主题的进一步讨论,请参阅以下帖子:
答案3
在 中,使用, Bourne 运算符的zsh
第三种${var::=value}
形式,其中赋值是无条件的(而不是仅当 var 未设置/为空时)。${var=value}
${var:=value}
for i in {1..5}; do echo ${x::=$i}; done
或者:
for i ({1..5}) echo ${x::=$i}
在bash
:
set -o posix # so the value of x remains after eval returns
for i in {1..5}; do x=$i eval 'echo "$x"'; done
也就是说,您需要$x
在评估包含其扩展的代码时进行设置。
对于像这里这样的十进制整数值,您还可以执行以下操作:
for i in {1..5}; do echo "$((x = i))"; done
或者,您始终可以${var:=value}
在设置x
为空字符串后使用 Bourne 运算符。
for i in {1..5}; do x=; echo "${x:=$i}"; done