我如何遍历由Bash中的variables定义的一系列数字?

当范围由variables给出时,如何迭代Bash中的一系列数字?

我知道我可以做到这一点(在Bash 文档中称为“序列expression式”):

for i in {1..5}; do echo $i; done 

这使:

1
2
3
4

然而,我怎样才能用variablesreplace范围端点? 这不起作用:

 END=5 for i in {1..$END}; do echo $i; done 

打印:

{} 1..5

 for i in $(seq 1 $END); do echo $i; done 

编辑:我更喜欢seq在其他方法,因为我实际上可以记住它;)

seq方法是最简单的,但是Bash内置了算术评估。

 END=5 for ((i=1;i<=END;i++)); do echo $i done # ==> outputs 1 2 3 4 5 on separate lines 

for ((expr1;expr2;expr3)); 像C语言和类似语言中的for (expr1;expr2;expr3)一样构造工作,和其他((expr))情况一样,Bash将它们视为算术。

讨论

正如Jiaaro所说,使用seq是很好的。 Pax Diablo提出了一个Bash循环来避免调用一个subprocess,如果$ END太大,还有更多的内存友好。 Zathrus在循环实现中发现了一个典型的错误,并且暗示说由于i是一个文本variables,所以连续转换来回数字会执行相关的减速操作。

整数算术

这是Bash循环的改进版本:

 typeset -ii END let END=5 i=1 while ((i<=END)); do echo $i … let i++ done 

如果我们想要的唯一的东西是echo ,那么我们可以写echo $((i++))

ephemient教给我一些东西:Bash允许for ((expr;expr;expr))构造。 因为我从来没有阅读Bash的整个手册页(就像我用Korn shell( ksh )手册页所做的那样),所以我错过了这一点。

所以,

 typeset -ii END # Let's be explicit for ((i=1;i<=END;++i)); do echo $i; done 

似乎是最有效的内存方式(不需要分配内存来消耗seq的输出,如果END非常大,这可能是一个问题),尽pipe可能不是“最快”的。

最初的问题

eschercycle指出,{ a .. b } Bash符号只适用于文字; 真的,相应的Bash手册。 我们可以通过一个没有exec()的单一(内部) fork()来克服这个障碍(就像调用seq ,这是另一个图像需要fork + exec一样):

 for i in $(eval echo "{1..$END}"); do 

evalecho都是Bash内build的,但是fork()是命令replace( $(…)构造)所必需的。

这就是为什么原始expression式不起作用。

男人bash

在任何其他扩展之前执行扩展扩展,并且在结果中保留任何特殊于其他扩展的字符。 这是严格的文本。 Bash不会对扩展或大括号之间的文本应用任何语法解释。

因此,在扩展参数之前, 大括号扩展是纯粹的文本macros操作

壳是macros处理器和更正式的编程语言之间高度优化的混合体。 为了优化典型用例,语言变得相当复杂,并且有一些限制被接受。

build议

我会build议坚持Posix 1function。 这意味着使用for i in <list>; do 如果列表已经知道,否则使用whileseq ,如:

 #!/bin/sh limit=4 i=1; while [ $i -le $limit ]; do echo $i i=$(($i + 1)) done # Or ----------------------- for i in $(seq 1 $limit); do echo $i done 

1. Bash是一个很好的shell,我交互使用它,但是我不把bash-isms写入我的脚本中。 脚本可能需要更快的shell,更安全,更embedded式。 他们可能需要在/ bin / sh上安装任何东西,然后就有了所有通常的标准参数。 还记得shellshock,又名bashdoor吗?

POSIX的方式

如果您关心可移植性,请使用POSIX标准中的示例 :

 i=2 end=5 while [ $i -le $end ]; do echo $i i=$(($i+1)) done 

输出:

 2 3 4 5 

不是 POSIX的东西:

  • (( ))没有美元,虽然它是POSIX自己提到的一个常见的扩展。
  • [[[这里够了。 另请参阅: Bash中的单方括号和双方括号有什么区别?
  • for ((;;))
  • seq (GNU Coreutils)
  • {start..end} ,并且不能像Bash手册中提到的那样使用variables。
  • let i=i+1 : POSIX 7 2.壳牌命令语言不包含单词let ,它在bash --posix失败bash --posix 4.3.42
  • 美元i=$i+1可能是必需的,但我不确定。 POSIX 7 2.6.4算术扩展说:

    如果shellvariablesx包含形成有效整数常量的值(可选地包括前导加号或减号),则算术扩展“$((x))”和“$(($ x))”将返回相同的值。

    但从字面上看,这并不意味着$((x+1))扩大,因为x+1不是一个variables。

另一层间接:

 for i in $(eval echo {1..$END}); do ∶ 

您可以使用

 for i in $(seq $END); do echo $i; done 

如果你在BSD / OS X上,你可以使用jot而不是seq:

 for i in $(jot $END); do echo $i; done 

这在bash正常工作:

 END=5 i=1 ; while [[ $i -le $END ]] ; do echo $i ((i = i + 1)) done 

我知道这个问题是关于bash ,但是 – 只是为了logging – ksh93更聪明,并按预期实现它:

 $ ksh -c 'i=5; for x in {1..$i}; do echo "$x"; done' 1 2 3 4 5 $ ksh -c 'echo $KSH_VERSION' Version JM 93u+ 2012-02-29 $ bash -c 'i=5; for x in {1..$i}; do echo "$x"; done' {1..5} 

这是另一种方式:

 end=5 for i in $(bash -c "echo {1..${end}}"); do echo $i; done 

这些都很好,但seq被认为是弃用,大多数只能用数字范围。

如果将for循环放在双引号中,则在回显string时将取消引用和结束variables,并且可以将该string发回BASH以执行。 $i需要用\ s进行转义,所以在发送到子shell之前不会对它进行评估。

 RANGE_START=a RANGE_END=z echo -e "for i in {$RANGE_START..$RANGE_END}; do echo \\${i}; done" | bash 

这个输出也可以分配给一个variables:

 VAR=`echo -e "for i in {$RANGE_START..$RANGE_END}; do echo \\${i}; done" | bash` 

这个应该产生的唯一“开销”应该是bash的第二个实例,所以它应该适合于密集的操作。

如果你需要它的前缀比你可能会喜欢这个

  for ((i=7;i<=12;i++)); do echo `printf "%2.0d\n" $i |sed "s/ /0/"`;done 

那将会屈服

 07 08 09 10 11 12 

(( ))replace{}

 tmpstart=0; tmpend=4; for (( i=$tmpstart; i<=$tmpend; i++ )) ; do echo $i ; done 

产量:

 0 1 2 3 4 

如果你正在执行shell命令,并且你(像我)对stream水线有迷恋,那么这个很好:

seq 1 $END | xargs -I {} echo {}

如果你想保持尽可能接近的括号expression式的语法,尝试从bash技巧的range.bash range函数 。

例如,以下所有内容与echo {1..10}完全相同:

 source range.bash one=1 ten=10 range {$one..$ten} range $one $ten range {1..$ten} range {1..10} 

它试图用尽可能less的“陷阱”来支持本地的bash语法:不仅variables被支持,而且通常不受欢迎的无效范围行为被作为string提供(例如for i in {1..a}; do echo $i; done )也被阻止。

其他答案在大多数情况下都可以使用,但是它们至less有下列缺点之一:

  • 它们中的许多使用子壳 ,这会损害性能,并且在一些系统上可能是不可能的 。
  • 他们中的许多人依靠外部程序。 即使seq是必须安装使用的二进制文件,必须由bash加载,并且必须包含您期望的程序,因为它在这种情况下工作。 无处不在,依靠的不仅仅是Bash语言本身。
  • 仅使用本地Bashfunction的解决scheme(如@ ephemient's)不适用于字母范围,如{a..z} ; 大括号扩展会。 但问题是关于数字的范围,所以这是一个狡辩。
  • 它们中的大多数与{1..10}大括号扩展范围语法在{1..10}上不相似,所以使用这两种语言的程序可能有点难以阅读。
  • @ bobbogo的答案使用了一些熟悉的语法,但如果$ENDvariables不是范围另一端的有效范围“bookend”,则会出现意想不到的情况。 例如,如果END=a ,则不会发生错误,并且逐字值{1..a}将被回显。 这也是Bash的默认行为 – 它往往是意想不到的。

免责声明:我是链接代码的作者。