Ich versuche, zwei Sequenzen in derselben Schleife in meiner Shell wie folgt 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?
Ich versuche, zwei Sequenzen in derselben Schleife in meiner Shell wie folgt 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?
Antworten:
Dafür brauchen Sie 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
( ) übergeben.for i in x y z; do stuff "$i"; done
Hier erhalten geschweifte Klammern {
}
die Shell, um Ihre Sequenzen zu einer Liste zu erweitern. Sie müssen nur ein Leerzeichen dazwischen setzen, da die Shell Listen mit Argumenten auf diese aufteilt.
echo
die Zahlen
touch
Dateien ausführen möchten , können sie dies einfach tun touch {1..15}.txt {20..25}.txt
, hier wird keine Schleife benötigt. Aber wenn es sich um mehrere Aktionen auf derselben Nummer handelt - OK, könnte dies eine Schleife verwenden.
Alternativ können wir verwenden seq
( eine Folge von Zahlen drucken ), 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 folgende 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"
Zannas Antwort und die Antwort von pa4080 sind beide gut und ich würde wahrscheinlich unter den meisten Umständen mit einem von ihnen gehen. Vielleicht ist es selbstverständlich, aber der Vollständigkeit halber werde ich es trotzdem sagen: Sie können jeden Wert in ein Array laden und dann das Array 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
Zannas Antwort ist absolut richtig und gut für Bash geeignet, aber wir können das 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 ARGUMENTS
größer als die Formatsteuerelemente in ist 'FORMAT STRING'
, printf
alle ARGUMENTS
in gleiche Blöcke aufgeteilt werden und diese immer wieder an die Formatzeichenfolge angepasst werden, bis die ARGUMENTS
Liste leer ist.
Wenn wir nach Portabilität streben, können wir printf "%d\n" $(seq 1 15) $(seq 20 25)
stattdessen verwenden
Lassen Sie uns das weiter gehen und mehr Spaß machen. Angenommen, wir möchten eine Aktion ausführen, anstatt nur Zahlen zu drucken. Um Dateien aus dieser Zahlenfolge zu erstellen, könnten wir dies leicht tun touch {1..15}.txt {20..25}.txt
. 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 Old-School-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
Wenn wir eine Skriptlösung erstellen möchten, die mit Shells ohne Klammererweiterung funktioniert (worauf es {1..15} {20..25}
ankommt), 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
.
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 tragbaren 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
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
Nur weil wir hier die Python-Lösung können
$ python3 -c 'print("\n".join([str(i) for i in (*range(1,16),*range(20,26))]))'
Oder mit etwas Muschel:
bash-4.3$ python3 << EOF
> for i in (*range(16),*range(20,26)):
> print(i)
> EOF
touch $(printf "%d\n" {1..15} {20..25})
:-)
bash
dich nicht mal $()
da, nur touch {1..15}.txt {20..25}.txt
:) Aber natürlich könnten wir printf "%d\n
{1..15} {20..25} `mit verwenden, xargs
wenn wir mehr als nur touch
Dateien machen wollen. Es gibt viele Möglichkeiten, Dinge zu tun, und es macht das Schreiben von Skripten so viel Spaß!