How do I iterate over a range of numbers defined by variables in Bash?
How do I iterate over a range of numbers in Bash when the range is given by a variable?
I know I can do this (called "sequence expression" in the Bash documentation):
for i in {1..5}; do echo $i; done
Which gives:
1
2
3
4
5
Yet, how can I replace either of the range endpoints with a variable? This doesn't work:
END=5
for i in {1..$END}; do echo $i; done
Which prints:
{1..5}
for i in $(seq 1 $END); do echo $i; done
编辑:我更喜欢seq
在其他方法,因为我实际上可以记住它;)
The seq
method is the simplest, but Bash has built-in arithmetic evaluation.
END=5
for ((i=1;i<=END;i++)); do
echo $i
done
# ==> outputs 1 2 3 4 5 on separate lines
The for ((expr1;expr2;expr3));
construct works just like for (expr1;expr2;expr3)
in C and similar languages, and like other ((expr))
cases, Bash treats them as arithmetic.
discussion
Using seq
is fine, as Jiaaro suggested. Pax Diablo suggested a Bash loop to avoid calling a subprocess, with the additional advantage of being more memory friendly if $END is too large. Zathrus spotted a typical bug in the loop implementation, and also hinted that since i
is a text variable, continuous conversions to-and-fro numbers are performed with an associated slow-down.
integer arithmetic
This is an improved version of the Bash loop:
typeset -i i END
let END=5 i=1
while ((i<=END)); do
echo $i
…
let i++
done
If the only thing that we want is the echo
, then we could write echo $((i++))
.
ephemient taught me something: Bash allows for ((expr;expr;expr))
constructs. Since I've never read the whole man page for Bash (like I've done with the Korn shell ( ksh
) man page, and that was a long time ago), I missed that.
So,
typeset -i i END # Let's be explicit
for ((i=1;i<=END;++i)); do echo $i; done
seems to be the most memory-efficient way (it won't be necessary to allocate memory to consume seq
's output, which could be a problem if END is very large), although probably not the “fastest”.
the initial question
eschercycle noted that the {a..b} Bash notation works only with literals; true, accordingly to the Bash manual. One can overcome this obstacle with a single (internal) fork()
without an exec()
(as is the case with calling seq
, which being another image requires a fork+exec):
for i in $(eval echo "{1..$END}"); do
Both eval
and echo
are Bash builtins, but a fork()
is required for the command substitution (the $(…)
construct).
上一篇: 线Bash无限while循环