无循环循环

无循环循环

我试图在 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和当然进行了测试。dashmkshbash


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

相关内容