bash - shell数组添加元素 - shell数组遍历
如何遍历Bash中由变量定义的一系列数字? (12)
讨论
正如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的整个手册页(就像我用Korn shell( ksh
)手册页完成的那样),但我错过了这一点。
所以,
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手册。 可以用一个没有exec()
的单一(内部) fork()
来克服这个障碍(就像调用seq
,这是另一个图像需要fork + exec一样):
for i in $(eval echo "{1..$END}"); do
eval
和echo
都是Bash buildins,但是命令替换( $(…)
构造)需要fork()
)。
当范围由变量给出时,如何迭代Bash中的一系列数字?
我知道我可以做到这一点(在Bash documentation称为“序列表达式”):
for i in {1..5}; do echo $i; done
这使:
1
2
3
4
五
然而,我怎样才能用变量替换范围端点? 这不起作用:
END=5
for i in {1..$END}; do echo $i; done
打印:
{} 1..5
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将它们视为算术。
另一层间接:
for i in $(eval echo {1..$END}); do
∶
如果你想尽可能的接近括号表达式的语法,可以试试bash- 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}
它试图用尽可能少的“陷阱”来支持本地bash语法:不仅变量被支持,而且通常不受欢迎的无效范围行为被作为字符串提供(例如, for i in {1..a}; do echo $i; done
)也被阻止。
其他答案在大多数情况下都能正常工作,但它们至少有下列缺点之一:
- 它们中的许多使用subshells ,这会损害性能,并且在某些系统上可能无法实现 。
- 他们中的许多人依靠外部程序。 即使
seq
是必须安装使用的二进制文件,必须由bash加载,并且必须包含您期望的程序,因为它在这种情况下起作用。 无处不在,依靠的不仅仅是Bash语言本身。 - 只使用本地Bash功能的解决方案(如@ ephemient's)不适用于字母范围,如
{a..z}
; 大括号扩展会。 但问题是关于数字的范围,所以这是一个狡辩。 - 它们中的大多数与
{1..10}
大括号扩展范围语法在{1..10}
上不相似,所以使用这两种语法的程序可能会比较难以阅读。 - @ bobbogo的答案使用了一些熟悉的语法,但如果
$END
变量不是范围另一端的有效范围“bookend”,则会出现意想不到的情况。 例如,如果END=a
,则不会发生错误,并且逐字值{1..a}
会被回显。 这也是Bash的默认行为 - 它通常是意想不到的。
免责声明:我是链接代码的作者。
如果你需要它的前缀,你可能会喜欢这个
for ((i=7;i<=12;i++)); do echo `printf "%2.0d\n" $i |sed "s/ /0/"`;done
那将会屈服
07
08
09
10
11
12
如果您使用BSD / OS X,则可以使用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}
用(( ))
替换{}
:
tmpstart=0;
tmpend=4;
for (( i=$tmpstart; i<=$tmpend; i++ )) ; do
echo $i ;
done
产量:
0
1
2
3
4
这在bash
正常工作:
END=5
i=1 ; while [[ $i -le $END ]] ; do
echo $i
((i = i + 1))
done
这在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"
}
这是另一种方式:
end=5
for i in $(bash -c "echo {1..${end}}"); do echo $i; done
for i in $(seq 1 $END); do echo $i; done
编辑:我更喜欢seq
在其他方法,因为我实际上可以记住它;)