Sintassi del ciclo "for" di script di shell


191

Ho ottenuto quanto segue per funzionare:

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

Produce una serie di linee di output: 2, output: 3, ecc.

Tuttavia, tentando di eseguire quanto segue:

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

produce quanto segue:

output: {2..10}

Come posso convincere il compilatore a rendersi conto che dovrebbe trattare $ max come l'altra estremità dell'array e non parte di una stringa?


2
quale sistema e shell stai usando? Che tipo di sistema sciocco ha sh o bash, ma non ha seq, un coreutil?
whatsisname

11
FreeBSD no.
Nietzche-jou,

echo "$idovrebbe essere echo "$i"- non risolverà il problema, però.
Dave Jarvis,

Piccolo stile nit: di solito vedo le parole chiave doe thensulla stessa riga di fore if, rispettivamente. Ad esempio,for i in {2..10}; do
un nerd pagato

Risposte:


234

L'espansione del controvento, {x..y} viene eseguita prima di altre espansioni, quindi non è possibile utilizzarla per sequenze di lunghezza variabile.

Invece, usa il seq 2 $maxmetodo come dichiarato dal mob dell'utente .

Quindi, per il tuo esempio sarebbe:

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

Non vi è alcuna buona ragione per usare un comando esterno come seqcontare e incrementare i numeri nel ciclo for, quindi si consiglia di evitare di usarlo seq. Questo comando è lasciato per compatibilità con il vecchio bash. I comandi integrati sono abbastanza veloci. for (( EXP1; EXP2; EXP3 )) ...
mugnaio

13
@miller la for (( ... ))sintassi non è POSIX e ciò significa ad esempio che non funzionerà immediatamente su cose come Alpine Linux. seqlo fa.
Wernight,

@Wernight Non solo Alpine Linux; #!/bin/shè Dash in Debian / Ubuntu.
Franklin Yu,

72

Prova la versione di espressione aritmetica 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 anche con Bourne shell (sh).


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, funzionava ancora. Forse la versione di sh conta? Sei su un vecchio Unix?
sistema PAUSA

8
Pochissimi sistemi ne hanno uno dedicato sh, rendendolo invece un collegamento ad un'altra shell. Idealmente, una shell invocata come shavrebbe solo sostenere quelle caratteristiche dello standard POSIX, ma probabilmente lasciate alcune delle loro caratteristiche extra attraverso. Il C-for-loop non è una funzione POSIX, ma potrebbe essere in shmodalità dalla shell reale.
Chepner,

27

Fai il ciclo manualmente:

i = 0
max = 10
mentre [$ i -lt $ max]
fare
    echo "output: $ i"
    true $ ((i ++))
fatto

Se non devi essere totalmente POSIX, puoi usare l'aritmetica per loop:

max = 10
per ((i = 0; i <max; i ++)); echo "output: $ i"; fatto

Oppure usa jot (1) su sistemi BSD:

per i in $ (jot 0 10); echo "output: $ i"; fatto

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

i punti e virgola non devono entrare in "for ((i = 0; i <max; i ++));"
logan

9

C'è più di un modo per farlo.

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

7
Un rischio per la sicurezza, poiché evalvaluterà tutto ciò che hai impostato max. Considera max="2}; echo ha; #", quindi sostituisci echo hacon qualcosa di più distruttivo.
Chepner,

6
(S) ha impostato un massimo di 10. Nessun rischio.
Conrad Meyer,

9

Se il seqcomando disponibile sul tuo sistema:

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

Altrimenti, usa il povero seqcon perl:

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

Guarda quelle virgolette.


L'ho appena verificato; non sembra che lo sia.
eykanal,

5

Questo è un modo:
Bash:

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

Il modo Bash sopra funziona per kshe zshanche quando bash -cviene sostituito con ksh -co zsh -crispettivamente.

Nota: for i in {2..${max}}; do echo $i; donefunziona in zshe ksh.


4

Possiamo iterare il loop come la programmazione C.

#!/bin/bash
for ((i=1; i<=20; i=i+1))
do 
      echo $i
done

2

Qui ha funzionato su Mac OS X.

Include l'esempio di una data BSD, come incrementare e decrementare la data anche:

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

2

Bene, poiché non avevo seqinstallato il comando sul mio sistema (Mac OS X v10.6.1 (Snow Leopard)), ho invece finito con un whileloop:

max=5
i=1

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

* Scrollate le spalle * Qualunque cosa funzioni .


2
seq è relativamente nuovo. L'ho scoperto solo pochi mesi fa. Ma puoi usare un ciclo 'for' !! Lo svantaggio di un 'while' è che devi ricordare di incrementare il contatore da qualche parte all'interno del loop, oppure loop verso il basso.
sistema PAUSA

1

Tutti fanno {1..8}e dovrebbero essere tutti POSIX. Inoltre non si romperanno se si inserisce un condizionale continuenel ciclo. 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

1

Uso:

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

È necessario il richiamo esplicito 'eval' per rivalutare la sostituzione di {} after variabile.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.