当范围由变量给定时,如何在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}


当前回答

如果你需要前缀,你可能会喜欢这个

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

这将产生

07
08
09
10
11
12

其他回答

如果您不想使用“seq”或“eval”或jot或算术扩展格式,例如for((i=1;i<=END;i++)),或其他循环,例如while,并且您不想只使用“printf”和“echo”,那么这个简单的解决方法可能适合您的预算:

a=1;b=5;d=‘对于{‘$a‘..‘$b‘}中的i;do echo-n“$i”;完成;'echo“$d”|bash

PS:反正我的bash没有“seq”命令。

在Mac OSX 10.6.8、Bash 3.2.48上测试

这在bash中运行良好:

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

有很多方法可以做到这一点,但我更喜欢的方法如下

使用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

这就是为什么最初的表达不起作用。

来自man bash:

在之前进行支撑扩展任何其他扩展,以及其他特殊字符扩展保留在后果它是严格的文本。猛击不应用任何语法对上下文的解释支撑。

因此,大括号扩展是在参数扩展之前作为纯文本宏操作完成的。

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

正式建议

我建议坚持使用Posix1功能。这意味着在<list>中使用for i;如果列表已经知道,则使用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。脚本可能需要更快的外壳、更安全的外壳和更嵌入式的外壳。他们可能需要在任何安装为/bin/sh的设备上运行,然后就有了所有常见的支持标准的论点。还记得shellshock,又名bashtoor吗?

我结合了这里的一些想法并衡量了性能。

TL;DR要点:

seq和{..}真的很快for和while循环很慢$()速度慢for((;;))循环较慢$(())甚至更慢担心内存中的N个数字(seq或{..})是愚蠢的(至少高达100万)

这些都不是结论。您必须查看这些代码背后的C代码才能得出结论。这更多的是关于我们如何使用这些机制来循环代码。大多数单次操作的速度都接近于相同的速度,在大多数情况下这并不重要。但是,像for((i=1;i<=1000000;i++))这样的机制有很多操作,正如您可以看到的那样。每个循环的操作数也比从i中得到的要多得多,单位为$(seq110000)。这对你来说可能并不明显,这就是为什么做这样的测试是有价值的。

演示

# show that seq is fast
$ time (seq 1 1000000 | wc)
 1000000 1000000 6888894

real    0m0.227s
user    0m0.239s
sys     0m0.008s

# show that {..} is fast
$ time (echo {1..1000000} | wc)
       1 1000000 6888896

real    0m1.778s
user    0m1.735s
sys     0m0.072s

# Show that for loops (even with a : noop) are slow
$ time (for i in {1..1000000} ; do :; done | wc)
       0       0       0

real    0m3.642s
user    0m3.582s
sys 0m0.057s

# show that echo is slow
$ time (for i in {1..1000000} ; do echo $i; done | wc)
 1000000 1000000 6888896

real    0m7.480s
user    0m6.803s
sys     0m2.580s

$ time (for i in $(seq 1 1000000) ; do echo $i; done | wc)
 1000000 1000000 6888894

real    0m7.029s
user    0m6.335s
sys     0m2.666s

# show that C-style for loops are slower
$ time (for (( i=1; i<=1000000; i++ )) ; do echo $i; done | wc)
 1000000 1000000 6888896

real    0m12.391s
user    0m11.069s
sys     0m3.437s

# show that arithmetic expansion is even slower
$ time (i=1; e=1000000; while [ $i -le $e ]; do echo $i; i=$(($i+1)); done | wc)
 1000000 1000000 6888896

real    0m19.696s
user    0m18.017s
sys     0m3.806s

$ time (i=1; e=1000000; while [ $i -le $e ]; do echo $i; ((i=i+1)); done | wc)
 1000000 1000000 6888896

real    0m18.629s
user    0m16.843s
sys     0m3.936s

$ time (i=1; e=1000000; while [ $i -le $e ]; do echo $((i++)); done | wc)
 1000000 1000000 6888896

real    0m17.012s
user    0m15.319s
sys     0m3.906s

# even a noop is slow
$ time (i=1; e=1000000; while [ $((i++)) -le $e ]; do :; done | wc)
       0       0       0

real    0m12.679s
user    0m11.658s
sys 0m1.004s