it-swarm.com.de

Führen Sie zwei Sequenzen in einer Schleife durch

Ich versuche, in meiner Shell zwei Sequenzen in derselben Schleife wie folgt durchzugehen:

#!/bin/bash
for i in (1..15) and (20..25) ;
do
     echo $i
     ......
     .....other process
done

hast du eine Idee, wie ich das erreichen kann?

8
HISI

Sie brauchen dafür nur eine Klammererweiterung

$ for n in {1..3} {200..203}; do echo $n; done
1
2
3
200
201
202
203

Wir können eine Liste an for (for i in x y z; do stuff "$i"; done) übergeben.

Hier veranlassen geschweifte Klammern {} die Shell, Ihre Sequenzen zu einer Liste zu erweitern. Sie brauchen nur ein Leerzeichen dazwischen zu setzen, da die Shell Listen von Argumenten auf diese aufteilt.

10
Zanna

Alternativ können wir seq (eine Folge von Zahlen ausgeben) verwenden. Hier sind zwei äquivalente Beispiele:

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

Wenn es sich um ein Skript handelt, können Sie für sich wiederholende Aufgaben Funktionen verwenden:

#!/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"
6
pa4080

Zannas Antwort und pa4080s Antwort sind beide gut und ich würde wahrscheinlich unter den meisten Umständen mit einer von ihnen gehen. Vielleicht versteht es sich von selbst, aber der Vollständigkeit halber sage ich es trotzdem: Sie können jeden Wert in ein Array laden und das Array dann in einer Schleife durchlaufen. Zum Beispiel:

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
GreenMatt

Looping ohne Schleife

Zannas Antwort ist absolut richtig und gut für Bash geeignet, aber wir können dies noch weiter verbessern, ohne eine Schleife zu verwenden.

printf "%d\n"  {1..15} {20..25}

Das Verhalten von printf ist so, dass, wenn die Anzahl von ARGUMENTS größer als die Anzahl der Formatsteuerelemente in 'FORMAT STRING' ist, printf alle ARGUMENTS in gleiche Blöcke aufteilt und behält Passen Sie sie immer wieder an die Formatzeichenfolge an, bis die Liste ARGUMENTS erschöpft ist.

Wenn wir Portabilität anstreben, können wir stattdessen printf "%d\n" $(seq 1 15) $(seq 20 25) verwenden

Lassen Sie uns das weiterführen und mehr Spaß machen. Angenommen, wir möchten eine Aktion ausführen, anstatt nur Zahlen zu drucken. Zum Erstellen von Dateien aus dieser Folge von Zahlen könnten wir einfach touch {1..15}.txt {20..25}.txt ausführen. Was ist, wenn mehrere Dinge auftreten sollen? Wir könnten auch so etwas machen:

$ printf "%d\n" {1..15} {20..25} | xargs -I % bash -c 'touch "$1.txt"; stat "$1.txt"' sh %

Oder wenn wir es im alten Stil machen wollen:

printf "%d\n" {1..15} {20..25} | while read -r line; do 
    touch "$line".txt;
    stat "$line".txt;
    rm "$line".txt; 
done

Portable, aber ausführliche Alternative

Wenn wir eine Skriptlösung erstellen möchten, die mit Shells ohne geschweifte Klammern funktioniert (worauf sich {1..15} {20..25} stützt), können wir eine einfache while-Schleife schreiben:

#!/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

Natürlich ist diese Lösung ausführlicher, einige Dinge könnten verkürzt werden, aber es funktioniert. Getestet mit ksh, dash, mksh und natürlich bash.


Bash C-Stil Schleife

Aber wenn wir eine Schleife bash-spezifisch machen wollten (aus welchem ​​Grund auch immer, vielleicht nicht nur drucken, sondern auch etwas mit diesen Zahlen machen), können wir dies auch tun (im Grunde C-Loop-Version der portablen Lösung):

last=15; for (( i=1; i<=last;i++ )); do printf "%d\n" "$i"; [[ $i -eq $last ]] && !  [[ $i -eq 25 ]] && { i=19;last=25;} ;done

Oder in besser lesbarem Format:

last=15
for (( i=1; i<=last;i++ )); 
do 
    printf "%d\n" "$i"
    [[ $i -eq $last ]] && !  [[ $i -eq 25 ]] && { i=19;last=25;} 
done

Performance-Vergleich verschiedener Looping-Ansätze

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

Nicht-Shell-Alternative

Nur weil wir es können, hier ist Python Lösung

$ python3 -c 'print("\n".join([str(i) for i in (*range(1,16),*range(20,26))]))'

Oder mit ein bisschen Shell:

bash-4.3$ python3 << EOF
> for i in (*range(16),*range(20,26)):
>    print(i)
> EOF
3