2009-09-18 16 views
133

ho ottenuto quanto segue al lavoro:Shell script "per" la sintassi ciclo

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

Produce una serie di linee di output: 2, output: 3, così via.

Tuttavia, cercando di eseguire il seguente:

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

produce il seguente:

output: {2..10} 

Come posso ottenere il compilatore di realizzarlo dovrebbe trattare $ max come l'altra estremità della matrice e non parte di una stringa?

+2

quale sistema e le coperture stai usando? Che tipo di sistema goofy ha sh o bash, ma non ha seq, un coreutil? – whatsisname

+9

FreeBSD no. –

+0

echo "$ i' dovrebbe essere" echo "$ i" '- non risolverà il problema, però. –

risposta

162

Espansione controvento, {x..y} viene eseguita prima di altre espansioni, quindi non è possibile utilizzarla per le sequenze di lunghezza variabile.

Utilizzare invece il metodo seq 2 $max come user mob stated.

Quindi, per il suo esempio sarebbe:

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

Non c'è una buona ragione per usare un comando esterno come 'seq' per contare e incrementare i numeri nel ciclo for, quindi si consiglia di evitare l'uso di' seq'. il comando è lasciato alla compatibilità con la vecchia bash. I comandi integrati sono abbastanza veloci. 'for ((EXP1; EXP2; EXP3)) ...' – miller

+6

@ miller la sintassi 'for ((...))' non è POSIX e ciò significa, ad esempio, che non funzionerà out of the box su cose come Alpine Linux. 'seq' fa. – Wernight

7

Se il comando seq disponibile nel sistema:

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

In caso contrario, quindi utilizzare povero seq con perl:

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

guardare quei virgolette.

+0

Ho appena controllato – eykanal

0

Beh, come non ho avuto il comando seq installato sul mio sistema (Mac OS     X v10.6.1   (Snow Leopard  )), ho finito per usare un ciclo while invece:

max=5 
i=1 

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

* Shrugs * quello che funziona.

+2

seq è relativamente nuovo. L'ho scoperto solo pochi mesi fa. Ma tu * puoi * usare un ciclo "per" !! Lo svantaggio di un "while" è che devi ricordare di incrementare il contatore da qualche parte all'interno del ciclo, oppure di eseguire il ciclo verso il basso. –

22

Passo il ciclo manualmente:

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

Se non c'è bisogno di essere totalmente POSIX, è possibile utilizzare l'aritmetica per ciclo:

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

Oppure utilizzare iota (1) su sistemi BSD:

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

+1 per jot, che dovrebbe essere disponibile su MacOS: http://developer.apple.com/mac/library/documentation/Darwin/Reference/ManPages/man1/jot.1.html –

+3

'true $ ((i ++)) 'non funziona in tutti i casi, quindi la maggior parte dei portatili sarebbe' true $ ((i = i + 1)) '. – Flow

+0

semi-colon non dovrebbe entrare in "for ((i = 0; i logan

47

Prova la versione aritmetica-espressione di for:

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

Questo è disponibile nella maggior parte delle versioni di bash, e dovrebbe essere compatibile Bourne shell (sh) anche.

+14

Buona risposta, ma questo non funziona con '#!/Bin/sh'. – Flow

+1

@Flow: Hm, l'ho appena provato su un paio di sistemi (basati su Linux e BSD) con #!/ bin/sh e ha funzionato bene. Invocato sotto bash e specificamente sotto/bin/sh, funziona ancora. Forse la versione di Sh importa? Sei su qualche vecchio Unix? –

+0

AFAIK QNX 6.3.0 – Flow

7

C'è più di un modo per farlo.

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

Un rischio per la sicurezza, dal momento che 'eval' valuterà * qualsiasi cosa * si imposta' max' su. Consideriamo 'max =" 2}; echo ha; # "', quindi sostituisci 'echo ha' con qualcosa di più distruttivo. – chepner

+4

(S) ha impostato su un massimo di 10. Nessun rischio. –

0

Usa:

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

È necessario il 'eval' esplicita chiamata a rivalutare il {} dopo la sostituzione di variabile.

1

Qui ha funzionato su Mac OS     X.

Esso comprende l'esempio di una data BSD, come aumentare e diminuire la data anche:

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

Questo è un modo:
bash:

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

Il modo in cui bash sopra funzionerà per ksh e zsh anche quando bash -c viene sostituito con ksh -c o zsh -c rispettivamente.

Nota: for i in {2..${max}}; do echo $i; done funziona in zsh e ksh.

1

Questi vanno tutti a {1..8} e dovrebbero essere tutti POSIX. Inoltre, non si romperanno se si imposta un ciclo condizionale continue. Il modo canonico:

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

Un altro modo:

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

e un altro:

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