Führt zwei Sequenzen in einer Schleife durch

8

Ich versuche, zwei Sequenzen in der gleichen Schleife in meiner Shell zu durchlaufen:

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

Irgendeine Idee, wie ich das erreichen kann?

    
HISI 24.10.2017, 14:52

4 Antworten

10

Sie brauchen nur eine Klammererweiterung dafür

$ 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.

Also, Klammern { } holen die Shell, um Ihre Sequenzen in eine Liste zu erweitern. Sie müssen nur ein Leerzeichen dazwischen setzen, da die Shell Listen von Argumenten auf diese verteilt.

    
Zanna 24.10.2017, 15:18
6

Alternativ können wir seq ( drucke eine Folge von Zahlen ) verwenden, hier sind zwei gleichwertige 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 ""; }
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  '; do echo "$i"; done; }
my_function "1" "3"
my_function "101" "103"
    
pa4080 24.10.2017 15:28
3

Schleife ohne Schleife

Zannas Antwort ist absolut korrekt und gut für bash geeignet, aber wir können das noch verbessern, ohne eine Schleife zu benutzen.

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

Das Verhalten von printf ist so, dass, wenn die Anzahl von ARGUMENTS größer als die Formatsteuerelemente in 'FORMAT STRING' ist, printf alle ARGUMENTS in gleiche Stücke teilt und sie an die Formatzeichenfolge anpasst und weiter, bis die ARGUMENTS list nicht mehr zur Verfügung steht.

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

Lasst uns das weiter und mehr Spaß machen. Angenommen, wir möchten eine Aktion durchführen, anstatt nur Zahlen zu drucken. Zum Erstellen von Dateien aus dieser Zahlenfolge könnten wir einfach touch {1..15}.txt {20..25}.txt verwenden. Was, wenn wir wollen, dass mehrere Dinge passieren? Wir könnten auch so etwas machen:

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

Oder wenn wir es altmodisch 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 arbeitet, die keine Klammererweiterung haben (auf die {1..15} {20..25} angewiesen ist), können wir eine einfache while-Schleife schreiben:

#!/bin/sh
start=
jump=
new_start=
end=

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-Style-Schleife

Aber wenn wir einen Loop Bash-spezifischen (aus welchem ​​Grund auch immer, vielleicht nicht nur Drucken, sondern auch etwas mit diesen Zahlen) machen möchten, 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

Leistungsvergleich verschiedener Schleifenansä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 hier die Python-Lösung finden können

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

Oder mit etwas Shell:

bash-4.3$ python3 << EOF
> for i in (*range(16),*range(20,26)):
>    print(i)
> EOF
    
Sergiy Kolodyazhnyy 26.10.2017 21:41

Tags und Links