我试图在 shell 中的同一个循环中运行两个序列,如下所示:
#!/bin/bash
for i in (1..15) and (20..25) ;
do
echo $i
......
.....other process
done
知道如何实现这个吗?
答案1
你只需要进行括号扩展即可
$ for n in {1..3} {200..203}; do echo $n; done
1
2
3
200
201
202
203
我们可以将一个列表传递给for
()。for i in x y z; do stuff "$i"; done
因此,这里括号{
}
使 shell 将序列扩展为列表。您只需在它们之间放置一个空格,因为 shell 会根据这些空格来拆分参数列表。
答案2
或者我们可以使用seq
(打印数字序列),下面是两个等效的例子:
for i in `seq 1 3` `seq 101 103`; do echo $i; done
for i in $(seq 1 3) $(seq 101 103); do echo $i; done
如果是脚本,对于重复任务,可以使用函数:
#!/bin/bash
my_function() { echo "$1"; }
for i in {1..3}; do my_function "$i"; done
for i in {101..103}; do my_function "$i"; done
#!/bin/bash
my_function() { for i in `seq $1 $2`; do echo "$i"; done; }
my_function "1" "3"
my_function "101" "103"
答案3
Zanna 的回答和pa4080 的回答都很好,在大多数情况下我可能会选择其中一种。也许不用多说,但为了完整起见,我还是要说:您可以将每个值加载到数组中,然后循环遍历该数组。例如:
the_array=( 1 2 3 4 5 6 7 8 9 10 20 21 22 23 24 25 )
for i in "${the_array[@]}";
do
echo $i
done
答案4
无循环循环
Zanna 的回答绝对正确,并且非常适合 bash,但我们可以在不使用循环的情况下进一步改进它。
printf "%d\n" {1..15} {20..25}
的行为printf
是这样的,如果的数量ARGUMENTS
大于中的格式控件'FORMAT STRING'
,那么printf
将全部分成ARGUMENTS
相等的块,并不断地将它们一遍又一遍地适合格式字符串,直到用完ARGUMENTS
列表。
如果我们追求便携性,我们可以printf "%d\n" $(seq 1 15) $(seq 20 25)
使用
让我们更进一步,更有趣。假设我们想执行一个操作,而不仅仅是打印数字。要从该数字序列创建文件,我们可以轻松地做到这一点touch {1..15}.txt {20..25}.txt
。如果我们想发生多件事怎么办?我们也可以这样做:
$ printf "%d\n" {1..15} {20..25} | xargs -I % bash -c 'touch "$1.txt"; stat "$1.txt"' sh %
或者如果我们想使它具有复古风格:
printf "%d\n" {1..15} {20..25} | while read -r line; do
touch "$line".txt;
stat "$line".txt;
rm "$line".txt;
done
便携但冗长的替代方案
如果我们想要制作一个适用于没有括号扩展(依赖{1..15} {20..25}
)的 shell 的脚本解决方案,我们可以编写一个简单的 while 循环:
#!/bin/sh
start=$1
jump=$2
new_start=$3
end=$4
i=$start
while [ $i -le $jump ]
do
printf "%d\n" "$i"
i=$((i+1))
if [ $i -eq $jump ] && ! [ $i -eq $end ];then
printf "%d\n" "$i"
i=$new_start
jump=$end
fi
done
当然,这个解决方案比较冗长,有些内容可以缩短,但它确实有效。已使用、、ksh
和当然进行了测试。dash
mksh
bash
Bash C 风格循环
但是如果我们想要制作一个特定于 bash 的循环(无论出于什么原因,可能不仅仅是打印,还对这些数字做一些事情),我们也可以这样做(基本上是可移植解决方案的 C 循环版本):
last=15; for (( i=1; i<=last;i++ )); do printf "%d\n" "$i"; [[ $i -eq $last ]] && ! [[ $i -eq 25 ]] && { i=19;last=25;} ;done
或者采用更易读的格式:
last=15
for (( i=1; i<=last;i++ ));
do
printf "%d\n" "$i"
[[ $i -eq $last ]] && ! [[ $i -eq 25 ]] && { i=19;last=25;}
done
不同循环方法的性能比较
bash-4.3$ time bash -c 'printf "%d\n" {0..50000}>/dev/null'
real 0m0.196s
user 0m0.124s
sys 0m0.028s
bash-4.3$ time bash -c 'for i in {1..50000}; do echo $i > /dev/null; done'
real 0m1.819s
user 0m1.328s
sys 0m0.476s
bash-4.3$ time bash -c ' i=0;while [ $i -le 50000 ]; do echo $i>/dev/null; i=$((i+1)); done'
real 0m3.069s
user 0m2.544s
sys 0m0.500s
bash-4.3$ time bash -c 'for i in $(seq 1 50000); do printf "%d\n" > /dev/null; done'
real 0m1.879s
user 0m1.344s
sys 0m0.520s
无壳替代方案
因为我们可以得到 Python 解决方案
$ python3 -c 'print("\n".join([str(i) for i in (*range(1,16),*range(20,26))]))'
或者用一点 shell:
bash-4.3$ python3 << EOF
> for i in (*range(16),*range(20,26)):
> print(i)
> EOF