Składnia pętli „for” skryptu powłoki

191

Do pracy dostałem następujące rzeczy:

for i in {2..10}
do
    echo "output: $i"
done

Produkuje kilka linii output: 2, output: 3tak dalej.

Jednak próba uruchomienia następujących czynności:

max=10
for i in {2..$max}
do
    echo "$i"
done

produkuje:

output: {2..10}

Jak sprawić, by kompilator zdał sobie sprawę, że $ max powinien traktować jako drugi koniec tablicy, a nie część ciągu?

eykanal
źródło
2
jakiego systemu i powłoki używasz? Jaki głupi system ma sh lub bash, ale nie ma seq, coreutil?
whatsisname
11
FreeBSD nie.
Nietzche-jou
echo "$ipowinno być echo "$i"- jednak nie rozwiąże problemu.
Dave Jarvis,
Małe styl nit: Zazwyczaj zobaczyć doi thensłowa kluczowe na tej samej linii, fori if, odpowiednio. Np.for i in {2..10}; do
opłacony frajer

Odpowiedzi:

234

Rozwinięcie nawiasu klamrowego {x..y} jest wykonywane przed innymi rozszerzeniami, więc nie można go użyć dla sekwencji o zmiennej długości.

Zamiast tego należy użyć seq 2 $maxmetody jako użytkownik mob stwierdził .

Na przykład dla przykładu:

max=10
for i in `seq 2 $max`
do
    echo "$i"
done
Jaka jest nazwa?
źródło
Nie ma dobrego powodu, aby używać zewnętrznego polecenia, takiego jak seqzliczanie i zwiększanie liczb w pętli for, dlatego zaleca się unikanie używania seq. To polecenie pozostawia się do zachowania zgodności ze starą wersją bash. Wbudowane polecenia są wystarczająco szybkie. for (( EXP1; EXP2; EXP3 )) ...
młynarz
13
@miller for (( ... ))składnia nie jest POSIX, co oznacza na przykład, że nie będzie działać od razu po zainstalowaniu w takich systemach jak Alpine Linux. seqrobi.
Wernight
@Wernight Nie tylko Alpine Linux; #!/bin/shjest Dash w Debian / Ubuntu.
Franklin Yu,
72

Wypróbuj wersję z wyrażeniem arytmetycznym for:

max=10
for (( i=2; i <= $max; ++i ))
do
    echo "$i"
done

Jest to dostępne w większości wersji bash i powinno być również kompatybilne z powłoką Bourne'a (sh).

system PAUZA
źródło
1
@Flow: Hm, właśnie wypróbowałem to na kilku systemach (opartych na Linuksie i BSD) z #! / Bin / sh i działało dobrze. Wywoływany pod bash, a konkretnie pod / bin / sh, nadal działał. Może wersja sh ma znaczenie? Czy używasz starego Unixa?
system PAUZA
8
Bardzo niewiele systemów ma dedykowane sh, zamiast tego link do innej powłoki. Najlepiej shbyłoby, gdyby wywołano taką powłokę, która obsługiwałaby tylko te funkcje w standardzie POSIX, ale domyślnie przepuszcza niektóre z ich dodatkowych funkcji. Pętla for-style w stylu C nie jest funkcją POSIX, ale może być w shtrybie przez rzeczywistą powłokę.
chepner
27

Krok ręcznie pętli:

i = 0
max = 10
podczas gdy [$ i -lt $ max]
robić
    echo „wyjście: $ i”
    true $ ((i ++))
Gotowe

Jeśli nie musisz być całkowicie POSIX, możesz użyć arytmetyki dla pętli:

max = 10
dla ((i = 0; i <max; i ++)); wykonaj echo "output: $ i"; Gotowe

Lub użyj jot (1) w systemach BSD:

dla i w $ (jot 0 10); wykonaj echo "output: $ i"; Gotowe
Nietzche-jou
źródło
3
true $(( i++ ))nie działa we wszystkich przypadkach, więc najbardziej przenośny byłby true $((i=i+1)).
Flow
średnik nie powinien wchodzić „dla ((i = 0; i <max; i ++));”
logan
9

Jest na to więcej niż jeden sposób.

max=10
for i in `eval "echo {2..$max}"`
do
    echo "$i"
done
efemeryczny
źródło
7
Zagrożenie bezpieczeństwa, ponieważ evaloceni wszystko, co ustawisz max. Zastanów się max="2}; echo ha; #", a następnie zamień na echo hacoś bardziej destrukcyjnego.
chepner
6
(S) ustawił maksimum na 10. Bez ryzyka.
Conrad Meyer
9

Jeśli seqpolecenie jest dostępne w systemie:

for i in `seq 2 $max`
do
  echo "output: $i"
done

Jeśli nie, użyj biednego człowieka seqz perl:

seq=`perl -e "\$,=' ';print 2..$max"`
for i in $seq
do
  echo "output: $i"
done

Obserwuj te znaki cudzysłowu.

tłum
źródło
Właśnie to sprawdziłem; nie wydaje się, że tak jest.
eykanal
5

Oto sposób:
Bash:

max=10
for i in $(bash -c "echo {2..${max}}"); do echo $i; done

Powyższy sposób Bash będzie działał dla kshi zsh, gdy bash -czostanie zastąpiony odpowiednio przez ksh -club zsh -c.

Uwaga: for i in {2..${max}}; do echo $i; donedziała w zshi ksh.

Jahid
źródło
4

Możemy iterować pętlę jak programowanie C.

#!/bin/bash
for ((i=1; i<=20; i=i+1))
do 
      echo $i
done
rashedcs
źródło
2

Tutaj działało na Mac OS X.

Obejmuje przykład daty BSD, jak również zwiększać i zmniejszać datę:

for ((i=28; i>=6 ; i--));
do
    dat=`date -v-${i}d -j "+%Y%m%d"` 
    echo $dat
done
minhas23
źródło
2

Ponieważ nie miałem seqzainstalowanego polecenia w moim systemie (Mac OS X 10.6.1 (Snow Leopard)), whilezamiast tego skorzystałem z pętli:

max=5
i=1

while [ $max -gt $i ]
do
    (stuff)
done

* Wzrusza ramionami * Cokolwiek działa.

eykanal
źródło
2
seq jest stosunkowo nowy. Dowiedziałem się o tym dopiero kilka miesięcy temu. Ale można użyć „dla” pętli !! Wadą „chwili” jest to, że musisz pamiętać o zwiększeniu licznika gdzieś wewnątrz pętli, albo pętli w dół.
system PAUSE
1

Wszystko to robi {1..8}i powinno być POSIX. Nie ulegną również uszkodzeniu, jeśli wpiszesz warunek continuew pętli. Kanoniczny sposób:

f=
while [ $((f+=1)) -le 8 ]
do
  echo $f
done

Inny sposób:

g=
while
  g=${g}1
  [ ${#g} -le 8 ]
do
  echo ${#g}
done

i kolejny:

set --
while
  set $* .
  [ ${#} -le 8 ]
do
  echo ${#}
done

źródło
1

Posługiwać się:

max=10
for i in `eval echo {2..$max}`
do
    echo $i
done

Potrzebujesz wyraźnego wywołania „eval”, aby ponownie ocenić {} po podstawieniu zmiennej.

Chris Dodd
źródło