我知道可以这样做(在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 in $(seq 1 $END); do echo $i; done
编辑:我更喜欢使用seq
方法,因为我可以真正记住它 ;)
seq $END
已足够,因为默认情况下从1开始。根据man seq
命令的解释:“如果省略FIRST
或INCREMENT
参数,则默认为1”。 - fedorquiseq
方法是最简单的,但是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 将它们视为算术表达式。
seq
。使用它! - bobbogo#!/bin/bash
。具体操作请参考该页面:https://wiki.ubuntu.com/DashAsBinSh#My_production_system_has_broken_and_I_just_want_to_get_it_back_up.21 - Melebius如Jiaaro建议的那样,使用seq
是可以的。Pax Diablo建议使用Bash循环来避免调用子进程,还有一个额外的好处是如果$END太大,则更节省内存。Zathrus发现了循环实现中的一个典型错误,并暗示由于i
是一个文本变量,因此需要进行连续的数字转换,这会导致慢速运行。
这是Bash循环的改进版本:
typeset -i i 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的man页面(像我以前读过的Korn shell (ksh
) man页面一样,而那是很久以前的事情了),所以我错过了这个知识点。
所以,
typeset -i i END # Let's be explicit
for ((i=1;i<=END;++i)); do echo $i; done
这似乎是最节省内存的方法(不需要分配内存来消耗seq
的输出,如果END非常大可能会成为一个问题),尽管可能不是“最快”的。
eschercycle指出,{a..b} Bash符号仅适用于字面值;根据Bash手册,这是正确的。可以通过单个(内部)fork()
而没有exec()
来克服这个障碍(就像调用seq
一样,它作为另一个映像需要一个fork+exec):
for i in $(eval echo "{1..$END}"); do
eval
和echo
都是Bash内置命令,但对于命令替换($(...)
结构),需要使用fork()
。
for ((i=$1;i<=$2;++i)); do echo $i; done
命令可以正常工作。因此,我认为命令行参数没有问题。你是指其他方面的问题吗? - tzottime for i in $(seq 100000); do :; done
更快! - F. Hauri - Give Up GitHub/bin/sh
(破折号,而不是Bash)。 - Top-Master这里是原始表达式不起作用的原因。
根据man bash:
在执行任何其他扩展之前,都会进行花括号扩展,并且对于其他扩展特殊的字符会保留在结果中。它是纯文本的宏操作。Bash 不会对扩展的上下文或括号之间的文本应用任何语法解释。
因此,花括号扩展是一种早期的纯文本宏操作,在参数扩展之前执行。
Shell是高度优化的宏处理器和更正式的编程语言之间的混合体。为了优化典型使用情况,语言变得更加复杂并接受一些限制。
建议
我建议坚持使用Posix1功能。如果列表已知,请使用 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
seq
相比,花括号扩展在大范围内并不能节省太多的内存。例如,echo {1..1000000} | wc
显示 echo 产生了 1 行、一百万个单词和 6,888,896 字节。尝试 seq 1 1000000 | wc
得到了一百万行、一百万个单词和 6,888,896 字节,并且使用 time
命令测量,速度也快了七倍以上。 - Georgewhile
方法:https://dev59.com/HXVC5IYBdhLWcg3w1E7w#31365662 但很高兴你同意 :-) - Ciro Santilli OurBigBook.comprintf“%02i \ n”$ i
而不是echo。对于while循环方法,请尝试printf“%02i \ n”$ ((i ++))
。格式字符串:“0”表示用零填充而不是空格,“2”是字段宽度,“i”表示整数,不要忘记换行符:) - JGurtzPOSIX的方式
如果你关心可移植性,使用来自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 手册所述。let i=i+1
:在POSIX 7 第 2 章“Shell 命令语言”中未包含 let
这个词,在 bash --posix
4.3.42 上失败了。
i=$i+1
处的美元符号可能是必需的,但我不确定。根据POSIX 7 第 2.6.4 节“算术扩展”:
如果 shell 变量 x 包含一个形成有效整数常量的值,可选包括前导加号或减号,则算术扩展 "$((x))" 和 "$(($x))" 返回相同的值。
但是从字面上理解,这并不意味着 $((x+1))
扩展,因为 x+1
不是一个变量。
x
,而不是整个表达式。$((x + 1))
是完全正确的。 - chepnerseq
有所不同(BSD的seq
允许您使用-t
设置序列终止字符串),但自FreeBSD 9.0和NetBSD 3.0以来,它们也拥有了seq
。 - Adrian Günter$((x+1))
和 $((x + 1))
解析的结果完全相同,因为当解析器将 x+1
分词时,它将被分成3个标记:x
、+
和1
。x
不是一个有效的数字标记,但它是一个有效的变量名标记,然而 x+
不是,因此被分开了。+
是一个有效的算术运算符标记,但 +1
不是,所以标记再次被分开。依此类推。 - Adrian Günter您可以使用
for i in $(seq $END); do echo $i; done
另一层间接性:
for i in $(eval echo {1..$END}); do
∶
for n in {001..10}; do echo $n; done
- undefined我将这里的一些想法结合在一起并进行了性能测试。
seq
和 {..}
非常快for
和 while
循环很慢$( )
很慢for (( ; ; ))
循环更慢$(( ))
更慢这些不是结论。您必须查看每个机制背后的C代码才能得出结论。这更多地涉及我们如何使用每个机制来循环代码。大多数单个操作在速度上足够接近,以至于在大多数情况下不会有影响。但是像for (( i=1; i<=1000000; i++ ))
这样的机制是许多操作,正如您可以看到的那样。它还比您从for i in $(seq 1 1000000)
获得的每个循环的操作多得多。这可能对您不明显,这就是为什么进行此类测试很有价值。
# 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
$(seq)
的速度与 {a..b}
差不多。此外,每个操作所需的时间也大致相同,因此对于每次循环迭代,它会增加约 4 微秒的时间。这里的操作是指循环体中的 _echo_、算术比较、递增等。这其中有什么令人惊讶的吗?谁在乎循环机制需要多长时间才能完成其工作——运行时很可能会被循环内容所主导。 - bobbogo如果您需要前缀,那么您可能会喜欢这个。
for ((i=7;i<=12;i++)); do echo `printf "%2.0d\n" $i |sed "s/ /0/"`;done
将产生
07
08
09
10
11
12
printf "%02d\n" $i
比 printf "%2.0d\n" $i |sed "s/ /0/"
更简单易懂吗? - zb226如果你在BSD / OS X上,可以使用jot替代seq:
for i in $(jot $END); do echo $i; done
seq
命令。 - dcowThe seq command first appeared in Plan 9 from Bell Labs. A seq command appeared in NetBSD 3.0, and ported to FreeBSD 9.0. This command was based on the command of the same name in Plan 9 from Bell Labs and the GNU core utilities. The GNU seq command first appeared in the 1.13 shell utilities release.
- dcow
for i in {01..10}; do echo $i; done
会输出像01, 02, 03, ..., 10
这样的数字。 - toprmyarray=('a' 'b' 'c'); for i in ${!myarray[@]}; do echo $i; done
(注意感叹号)。 这比原来的问题更具体,但可能会有所帮助。 参见 bash 参数扩展。 - PlasmaBinturong