当范围由变量给定时,如何在Bash中迭代一系列数字?
我知道我可以做到这一点(在Bash文档中称为“序列表达式”):
for i in {1..5}; do echo $i; done
这给出了:
1.2.3.4.5.
然而,如何用变量替换任意一个范围端点?这不起作用:
END=5
for i in {1..$END}; do echo $i; done
哪些打印:
{1..5}
当范围由变量给定时,如何在Bash中迭代一系列数字?
我知道我可以做到这一点(在Bash文档中称为“序列表达式”):
for i in {1..5}; do echo $i; done
这给出了:
1.2.3.4.5.
然而,如何用变量替换任意一个范围端点?这不起作用:
END=5
for i in {1..$END}; do echo $i; done
哪些打印:
{1..5}
当前回答
有很多方法可以做到这一点,但我更喜欢的方法如下
使用seq
man-seq简介
$ seq [-w] [-f format] [-s string] [-t string] [first [incr]] last
语法
完整命令seq第一次递增最后一次
first是序列中的起始编号[可选,默认为:1]incr是增量[是可选的,默认为:1]last是序列中的最后一个数字
例子:
$ seq 1 2 10
1 3 5 7 9
仅使用第一个和最后一个:
$ seq 1 5
1 2 3 4 5
仅最后一个:
$ seq 5
1 2 3 4 5
使用{first..last..incr}
这里第一个和最后一个是强制性的,incr是可选的
只使用第一个和最后一个
$ echo {1..5}
1 2 3 4 5
使用incr
$ echo {1..10..2}
1 3 5 7 9
您甚至可以对以下字符使用此选项
$ echo {a..z}
a b c d e f g h i j k l m n o p q r s t u v w x y z
其他回答
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将它们视为算术。
如果希望尽可能接近大括号表达式语法,请尝试使用bash-tricks的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}
它试图用尽可能少的“gotchas”来支持本机bash语法:不仅支持变量,而且还防止了作为字符串提供的无效范围(例如,对于{1..a}中的i;do echo$i;done)的通常不希望的行为。
其他答案在大多数情况下都有效,但它们都至少有以下缺点之一:
其中许多都使用子外壳,这可能会损害性能,在某些系统上可能不可能。他们中的许多人依赖外部程序。甚至seq也是一个二进制文件,必须安装才能使用,必须由bash加载,并且必须包含您期望的程序,才能在这种情况下工作。无论是否无处不在,这比Bash语言本身更值得依赖。只使用本机Bash功能的解决方案,如@ephemient,将无法在字母范围内工作,如{a..z};支架膨胀将。不过,问题是关于数字的范围,所以这是一个狡辩。它们中的大多数在视觉上与{1..10}大括号扩展范围语法并不相似,因此使用这两种语法的程序可能有点难以阅读。@bobobogo的答案使用了一些熟悉的语法,但如果$END变量不是范围另一侧的有效范围“bookend”,则会出现一些意外情况。例如,如果END=a,则不会发生错误,并且将回显逐字值{1..a}。这也是Bash的默认行为——这通常是意外的。
免责声明:我是链接代码的作者。
我知道这个问题是关于bash的,但为了记录在案,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}
如果你需要前缀,你可能会喜欢这个
for ((i=7;i<=12;i++)); do echo `printf "%2.0d\n" $i |sed "s/ /0/"`;done
这将产生
07
08
09
10
11
12
这适用于Bash和Korn,也可以从高到低。可能不是最快或最漂亮的,但效果足够好。也处理负片。
function num_range {
# Return a range of whole numbers from beginning value to ending value.
# >>> num_range start end
# start: Whole number to start with.
# end: Whole number to end with.
typeset s e v
s=${1}
e=${2}
if (( ${e} >= ${s} )); then
v=${s}
while (( ${v} <= ${e} )); do
echo ${v}
((v=v+1))
done
elif (( ${e} < ${s} )); then
v=${s}
while (( ${v} >= ${e} )); do
echo ${v}
((v=v-1))
done
fi
}
function test_num_range {
num_range 1 3 | egrep "1|2|3" | assert_lc 3
num_range 1 3 | head -1 | assert_eq 1
num_range -1 1 | head -1 | assert_eq "-1"
num_range 3 1 | egrep "1|2|3" | assert_lc 3
num_range 3 1 | head -1 | assert_eq 3
num_range 1 -1 | tail -1 | assert_eq "-1"
}