2009-09-18 8 views
133

Dostałem następujące do pracy:Shell skrypt pętli „for” składni

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

Produkuje kilka linii output: 2, output: 3, itd.

jednak próbuje uruchomić następujące:

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

produkuje następujące:

output: {2..10} 

Jak mogę uzyskać kompilatora do realizacji należy traktować $ max jako drugiego końca tablicy , a nie część ciągu?

+2

jaki system i powłoki używasz? Jaki głupi system ma sh lub bash, ale nie ma seq, coreutil? – whatsisname

+9

FreeBSD nie. –

+0

'echo" $ i' powinno być 'echo" $ i "' - nie rozwiąże problemu, jednak. –

Odpowiedz

162

ekspansja Brace, {x..y} wykonywana jest przed innymi rozszerzeniami, więc nie można użyć dla sekwencji zmiennych długości.

Zamiast tego należy użyć metody seq 2 $max jako user mob stated.

Tak, dla przykładu byłoby:

max=10 
for i in `seq 2 $max` 
do 
    echo "$i" 
done 
+0

Nie ma żadnego powodu, aby używać zewnętrznego polecenia, takiego jak 'seq' do zliczania i zwiększania liczb w pętli for, dlatego zaleca się unikanie używania' seq'. Polecenie zostało pozostawione dla kompatybilności ze starym bashem.Wbudowane komendy są wystarczająco szybkie: 'for ((EXP1; EXP2; EXP3)) ...' – miller

+6

@ miller składnia 'for ((...))' nie jest POSIX-em, a to oznacza na przykład, że nie będzie działać po wyjęciu z pudełka, jak na przykład Alpine Linux. 'seq' robi. – Wernight

7

Jeśli komenda seq dostępne w systemie:

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

Jeśli nie, użyj biedaka seq z perl:

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

Oglądać te cudzysłowy.

+0

Właśnie sprawdziłem to, nie wydaje się, że to jest. – eykanal

0

Dobrze, że nie mam komenda seq zainstalowany w moim systemie (Mac OS       X v10.6.1 (Snow Leopard  )), skończyło się za pomocą while pętlę zamiast:

max=5 
i=1 

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

* wzrusza * co działa.

+2

seq jest stosunkowo nowy. Dowiedziałem się o tym kilka miesięcy temu. Ale ty * możesz * użyć pętli 'for' !! Wadą "chwili" jest to, że musisz pamiętać, aby zwiększyć licznik gdzieś w pętli, lub w pętli w dół. –

22

Krok pętla ręcznie:

 
i=0 
max=10 
while [ $i -lt $max ] 
do 
    echo "output: $i" 
    true $((i++)) 
done 

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

 
max=10 
for ((i=0; i < max; i++)); do echo "output: $i"; done 

Albo użyć jot (1) w systemach BSD:

 
for i in $(jot 0 10); do echo "output: $i"; done 
+1

+1 za jot, który powinien być dostępny na MacOS: http://developer.apple.com/mac/library/documentation/Darwin/Reference/ManPages/man1/jot.1.html –

+3

'true $ ((i ++)) 'nie działa we wszystkich przypadkach, więc większość przenośnych byłoby' true $ ((i = i + 1)) '. – Flow

+0

średnik nie powinien pochodzić z "for ((i = 0; i logan

47

Wypróbuj wersję arytmetyka ekspresja for:

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

ta jest dostępna w większości wersji bash i powinny być Bourne shell (sh) kompatybilny również.

+14

Dobra odpowiedź, ale to nie działa z '#!/Bin/sh'. – Flow

+1

@Flow: Hm, właśnie wypróbowałem to na kilku systemach (opartych na systemie Linux i BSD) z #!/ bin/sh i działało dobrze. Wywoływane w trybie bash, a konkretnie w/bin/sh, nadal działało. Może wersja Sh jest ważna? Czy jesteś na starym Unixie? –

+0

AFAIK QNX 6.3.0 – Flow

7

Istnieje więcej niż jeden sposób, aby to zrobić.

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

Zagrożenie bezpieczeństwa, ponieważ 'eval' oceni * wszystko * ustawiłeś' maks. 'Na. Rozważmy 'max =" 2}; echo ha; # "', a następnie zastąp 'echo ha' coś bardziej destrukcyjnego. – chepner

+4

(S) ustawił maks. Na 10. Bez ryzyka. –

0

Zastosowanie:

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

Trzeba wyraźnej 'eval' zadzwoń do przewartościowania w {} po podstawienia zmiennej.

1

Tu pracował na Mac OS     X.

Obejmuje ona na przykład daty BSD, jak zwiększyć i zmniejszyć datę również:

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

Jest to sposób:
Bash:

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

powyższy Bash sposób będzie pracować dla ksh i zsh także, gdy bash -c jest zastąpione odpowiednio ksh -c lub zsh -c.

Uwaga: for i in {2..${max}}; do echo $i; done działa w zsh i ksh.

1

Wszystkie te wartości mają wartość {1..8} i powinny być zgodne z POSIX. Nie ulegną one również zerwaniu, jeśli umieści w pętli warunkowe continue. Sposób kanoniczna:

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

Innym sposobem:

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

i drugiego:

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