当变量给出范围时,如何在Bash中迭代一系列数字?
我知道我可以这样做(在Bash documentation中称为"sequence expression"):
for i in {1..5}; do echo $i; done
这使:
1 2 3 4 5
但是,如何用变量替换任何一个范围 endpoints ?这不起作用:
END=5
for i in {1..$END}; do echo $i; done
哪个印刷品:
{1..5}
当变量给出范围时,如何在Bash中迭代一系列数字?
我知道我可以这样做(在Bash documentation中称为"sequence expression"):
for i in {1..5}; do echo $i; done
这使:
1 2 3 4 5
但是,如何用变量替换任何一个范围 endpoints ?这不起作用:
END=5
for i in {1..$END}; do echo $i; done
哪个印刷品:
{1..5}
17 回答
如果您使用的是BSD / OS X,则可以使用jot而不是seq:
您可以使用
编辑:我更喜欢
seq
而不是其他方法,因为我实际上可以记住它;)讨论
正如贾加所建议的,使用
seq
是好的 . Pax Diablo建议使用Bash循环以避免调用子进程,如果$ END太大,还有额外的优点:更友好 . Zathrus在循环实现中发现了一个典型的错误,并且还暗示由于i
是一个文本变量,因此使用相关的减速执行连续转换的往返数字 .整数运算
这是Bash循环的改进版本:
如果我们想要的唯一的东西是
echo
,那么我们可以写echo $((i++))
.ephemient教会了我一些东西:Bash允许
for ((expr;expr;expr))
构造 . 由于我已经完成了Korn shell(ksh
)手册页,那是很久以前的事了),我错过了 .所以,
似乎是最节省内存的方式(没有必要分配内存来消耗
seq
的输出,如果END非常大,这可能是一个问题),虽然可能不是“最快的” .最初的问题
eschercycle注意到 Bash表示法只适用于文字;是的,相应于Bash手册 . 没有
exec()
的单个(内部)fork()
可以克服这个障碍(调用seq
就是这种情况,这是另一个图像需要fork exec):eval
和echo
都是Bash内置函数,但命令替换($(…)
结构)需要fork()
.另一层间接:
The POSIX way
如果您关心可移植性,请使用example from the POSIX standard:
输出:
不是POSIX的东西:
(( ))
没有美元,虽然它是一个共同的延伸as mentioned by POSIX itself .[[
.[
就够了 . 另见:What is the difference between single and double square brackets in Bash?for ((;;))
seq
(GNU Coreutils){start..end}
,这不能用于提及by the Bash manual的变量 .let i=i+1
:POSIX 7 2. Shell Command Language不包含单词let
,并且在bash --posix
上失败.4.3.42美元可能需要
i=$i+1
,但我不确定 . POSIX 7 2.6.4 Arithmetic Expansion说:但从字面上读它并不意味着自
x+1
以来$((x+1))
扩展并不是一个变量 .如果您希望尽可能接近大括号表达式语法,请尝试range function from bash-tricks' range.bash .
例如,以下所有内容与
echo {1..10}
完全相同:它尝试使用尽可能少的"gotchas"来支持本机bash语法:不仅支持变量,而且还防止了作为字符串(例如
for i in {1..a}; do echo $i; done
)提供的无效范围的常常不合需要的行为 .其他答案在大多数情况下都有效,但它们都至少有以下缺点之一:
其中许多人使用subshells,在某些系统上可以使用harm performance和may not be possible .
他们中的许多人依赖外部程序 . 甚至
seq
是必须安装才能使用的二进制文件,必须由bash加载,并且必须包含您期望的程序,以便在这种情况下工作 . 无论是否普遍存在,除了Bash语言本身之外,还有更多的东西需要依赖 .仅使用本机Bash功能的解决方案,如@ ephemient,将不适用于字母范围,如
{a..z}
;支撑扩张将 . 问题是关于数字的范围,所以这是一个狡辩 .它们中的大多数在视觉上与
{1..10}
大括号扩展范围语法不相似,因此使用两者的程序可能更难以阅读 .@ bobbogo的答案使用了一些熟悉的语法,但如果
$END
变量不是该范围另一侧的有效范围"bookend",则会出现意外情况 . 例如,如果END=a
,则不会发生错误,并且将回显逐字值{1..a}
. 这也是Bash的默认行为 - 它通常是意料之外的 .免责声明:我是链接代码的作者 .
将
{}
替换为(( ))
:产量:
seq
方法是最简单的,但Bash具有内置的算术评估 .for ((expr1;expr2;expr3));
构造与C中的for (expr1;expr2;expr3)
类似语言,和其他((expr))
情况一样,Bash将它们视为算术 .如果你正在做shell命令而且你(像我一样)对流水线有一种迷恋,那么这个很好:
seq 1 $END | xargs -I {} echo {}
这些都很好,但seq应该被弃用,大多数只适用于数值范围 .
如果用双引号括起for循环,则在回显字符串时将取消引用开始和结束变量,并且可以将字符串右回送到BASH执行 .
$i
需要使用\来进行转义,因此在发送到子shell之前不会对其进行评估 .此输出也可以分配给变量:
这应该生成的唯一“开销”应该是bash的第二个实例,因此它应该适用于密集操作 .
这适用于Bash和Korn,也可以从较高的数字到较低的数字 . 可能不是最快或最漂亮,但运作良好 . 处理否定也是如此 .
这在
bash
中工作正常:这是另一种方式:
这就是原始表达不起作用的原因 .
来自man bash:
因此,在参数扩展之前,大括号扩展是一种纯粹的文本宏操作 .
Shell是宏处理器和更正式的编程语言之间高度优化的混合 . 为了优化典型的用例,语言变得更加复杂,并且接受了一些限制 .
我建议坚持使用Posix1功能 . 这意味着使用
for i in <list>; do
,如果列表已知,否则,请使用while
或seq
,如下所示:如果你需要它的前缀比你想要的更多
会产生的
我知道这个问题是关于
bash
,但是 - 只是为了记录 -ksh93
更聪明并按预期实现它: