我如何遍历由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
eval
和echo
都是Bash内build的,但是fork()
是命令replace( $(…)
构造)所必需的。
这就是为什么原始expression式不起作用。
从男人bash :
在任何其他扩展之前执行扩展扩展,并且在结果中保留任何特殊于其他扩展的字符。 这是严格的文本。 Bash不会对扩展或大括号之间的文本应用任何语法解释。
因此,在扩展参数之前, 大括号扩展是纯粹的文本macros操作。
壳是macros处理器和更正式的编程语言之间高度优化的混合体。 为了优化典型用例,语言变得相当复杂,并且有一些限制被接受。
build议
我会build议坚持Posix 1function。 这意味着使用for i in <list>; do
如果列表已经知道,否则使用while
或seq
,如:
#!/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的答案使用了一些熟悉的语法,但如果
$END
variables不是范围另一端的有效范围“bookend”,则会出现意想不到的情况。 例如,如果END=a
,则不会发生错误,并且逐字值{1..a}
将被回显。 这也是Bash的默认行为 – 它往往是意想不到的。
免责声明:我是链接代码的作者。