Przeprowadź dwie sekwencje w jednej pętli

8

Próbuję uruchomić dwie sekwencje w tej samej pętli w mojej powłoce, jak poniżej:

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

jakiś pomysł, jak mogę to osiągnąć?

HISI
źródło
@zanna - moja pierwsza myśl jest taka, że ​​boolean „i” jest wyłączny, co oznacza, że ​​wynikiem są liczby występujące w obu zestawach; czego w tym przypadku nie ma. Czy dostępne są słowa „i”?
pustkowie
1
@ravery I put "i" tylko po to, aby wyjaśnić, czego szukam
HISI
2
@YassineSihi - Dobrze zanotuj. Wielu nowych programistów natknie się na ten punkt, dopóki nie będzie w stanie przekwalifikować swojego mózgu, ponieważ język mówiony używa „i” włącznie, ale logicznie ”i„ jest wyłączny w większości języków programowania.
pustkowie

Odpowiedzi:

10

Potrzebujesz do tego tylko rozszerzenia nawiasów klamrowych

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

Możemy przekazać listę do for( ).for i in x y z; do stuff "$i"; done

Zatem nawiasy klamrowe { }pobierają powłokę, aby rozwinąć sekwencje w listę. Musisz tylko umieścić między nimi spację, ponieważ powłoka dzieli na nich listy argumentów.

Zanna
źródło
Tak, szelki. . . I nie potrzebujesz nawet pętli do tego ^ _0
Sergiy Kolodyazhnyy
@SergiyKolodyazhnyy Myślę, że tak naprawdę nie chcą tylko echoliczb
Zanna
tak, jeśli chcą jakiejś akcji, takiej jak touchpliki, mogą po prostu zrobić touch {1..15}.txt {20..25}.txt, nie ma potrzeby tutaj pętli. Ale oczywiście, jeśli jest to wiele akcji na tym samym numerze - OK, można użyć pętli.
Sergiy Kolodyazhnyy
6

Alternatywnie możemy użyć seq( wydrukować sekwencję liczb ), oto dwa równoważne przykłady:

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

Jeśli jest to skrypt, do powtarzalnych zadań możesz użyć funkcji:

#!/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"
pa4080
źródło
4

Odpowiedzi Zanny i pa4080 są dobre i prawdopodobnie wybrałbym jedną z nich w większości przypadków. Być może jest to oczywiste, ale ze względu na kompletność powiem to i tak: Możesz załadować każdą wartość do tablicy, a następnie zapętlić ją. Na przykład:

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
GreenMatt
źródło
@SergiyKolodyazhnyy: Dzięki opinii. Jestem na tyle stary, że tak mnie nauczono, i nadal zwykle robię to przy rzadkiej okazji, gdy piszę skrypt powłoki. Zaktualizowałem jednak odpowiedź, aby użyć tablicy.
GreenMatt
Bardzo dobrze ! Miłego pisania skryptów!
Sergiy Kolodyazhnyy
3

Pętla bez pętli

Odpowiedź Zanny jest absolutnie poprawna i dobrze nadaje się do bash, ale możemy to poprawić jeszcze bardziej bez użycia pętli.

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

Zachowanie się printfjest takie, że jeśli liczba ARGUMENTSjest większa niż formanty w formacie 'FORMAT STRING', wówczas printfwszystkie zostaną podzielone ARGUMENTS na równe części i będą ciągle dopasowywać je do ciągu formatu, aż skończy się ARGUMENTSlista.

Jeśli dążymy do przenośności, możemy printf "%d\n" $(seq 1 15) $(seq 20 25)zamiast tego wykorzystać

Weźmy to dalej i więcej zabawy. Powiedzmy, że chcemy wykonać akcję, a nie tylko drukować liczby. Możemy z łatwością zrobić pliki z tej sekwencji liczb touch {1..15}.txt {20..25}.txt. Co jeśli chcemy, aby wydarzyło się wiele rzeczy? Możemy również zrobić coś takiego:

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

Lub jeśli chcemy, aby był to oldschoolowy styl:

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

Przenośna, ale pełna alternatywy

Jeśli chcemy stworzyć rozwiązanie skryptowe, które będzie działać z powłokami, które nie mają rozszerzenia nawiasów klamrowych (na czym to {1..15} {20..25}polega), możemy napisać prostą pętlę 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

Oczywiście to rozwiązanie jest bardziej szczegółowe, niektóre rzeczy można skrócić, ale działa. Testowane z ksh, dash, mkshi, oczywiście bash.


Pętla Bash w stylu C.

Ale jeśli chcielibyśmy stworzyć pętlę specyficzną dla bash (z jakiegokolwiek powodu, być może nie tylko drukowania, ale także robienia czegoś z tymi liczbami), możemy również to zrobić (w zasadzie wersja przenośnego rozwiązania z pętlą 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

Lub w bardziej czytelnym formacie:

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

Porównanie wydajności różnych metod zapętlania

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

Alternatywa bez powłoki

Tylko dlatego, że możemy tutaj znaleźć rozwiązanie Python

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

Lub z odrobiną powłoki:

bash-4.3$ python3 << EOF
> for i in (*range(16),*range(20,26)):
>    print(i)
> EOF
Sergiy Kolodyazhnyy
źródło
1
Właśnie przetestowałem touch $(printf "%d\n" {1..15} {20..25}):-)
pa4080,
1
@ pa4080 właściwie dla bashciebie nawet $()tam nie potrzebujesz , po prostu touch {1..15}.txt {20..25}.txt :) Ale oczywiście moglibyśmy użyć printf "%d\n{1..15} {20..25} `z xargs, gdybyśmy chcieli zrobić coś więcej niż tylko touchpliki. Jest wiele sposobów na robienie rzeczy, dzięki czemu pisanie skryptów sprawia tyle radości!
Sergiy Kolodyazhnyy