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ąć?
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ąć?
Odpowiedzi:
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.
echo
liczb
touch
pliki, 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.
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"
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
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ę printf
jest takie, że jeśli liczba ARGUMENTS
jest większa niż formanty w formacie 'FORMAT STRING'
, wówczas printf
wszystkie zostaną podzielone ARGUMENTS
na równe części i będą ciągle dopasowywać je do ciągu formatu, aż skończy się ARGUMENTS
lista.
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
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
, mksh
i, oczywiście bash
.
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
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
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
touch $(printf "%d\n" {1..15} {20..25})
:-)
bash
ciebie 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 touch
pliki. Jest wiele sposobów na robienie rzeczy, dzięki czemu pisanie skryptów sprawia tyle radości!