当范围由变量给定时,如何在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
其他回答
如果您使用BSD/OSX,您可以使用jot而不是seq:
for i in $(jot $END); do echo $i; done
我知道这个问题是关于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}
POSIX方式
如果您关心可移植性,请使用POSIX标准中的示例:
i=2
end=5
while [ $i -le $end ]; do
echo $i
i=$(($i+1))
done
输出:
2
3
4
5
非POSIX的事物:
(())没有美元,尽管它是POSIX本身提到的常见扩展。[[.]在这里就足够了。另请参见:Bash中的单方括号和双方括号之间的区别是什么?用于((;;))seq(GNU内核){start..end},并且无法使用Bash手册中提到的变量。设i=i+1:POSIX 7 2。Shell命令语言不包含单词let,并且在bash上失败--posix 4.3.42i=$i+1的美元可能是必需的,但我不确定。POSIX 7 2.6.4算术扩展表示:如果外壳变量x包含形成有效整数常量的值,可选地包括前导加号或减号,则算术展开式“$((x))”和“$($x)”应返回相同的值。但从字面上看,这并不意味着$((x+1))会膨胀,因为x+1不是一个变量。
这些都很好,但seq被认为是不可取的,而且大多数只适用于数字范围。
如果将for循环括在双引号中,则当回显字符串时,开始变量和结束变量将被取消引用,并且可以将字符串直接发送回BASH执行$我需要用\来转义,所以在被发送到子shell之前不会进行评估。
RANGE_START=a
RANGE_END=z
echo -e "for i in {$RANGE_START..$RANGE_END}; do echo \\${i}; done" | bash
此输出也可以分配给变量:
VAR=`echo -e "for i in {$RANGE_START..$RANGE_END}; do echo \\${i}; done" | bash`
这应该产生的唯一“开销”应该是bash的第二个实例,因此它应该适合密集型操作。
这适用于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"
}