Sintaxis de bucle de script de shell "for"

190

He conseguido lo siguiente para trabajar:

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

Produce un montón de líneas de output: 2,output: 3 , etcétera.

Sin embargo, tratando de ejecutar lo siguiente:

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

produce lo siguiente:

output: {2..10}

¿Cómo puedo hacer que el compilador se dé cuenta de que debería tratar $ max como el otro extremo de la matriz y no como parte de una cadena?

eykanal
fuente
2
¿Qué sistema y shell estás usando? ¿Qué tipo de sistema tonto tiene sh o bash, pero no tiene seq, un coreutil?
whatsisname
11
FreeBSD no lo hace.
Nietzche-jou
echo "$idebería ser echo "$i", sin embargo, no solucionará el problema.
Dave Jarvis
Estilo pequeño: normalmente veo las palabras clave doy thenen la misma línea que fory if, respectivamente. Por ejemplo,for i in {2..10}; do
un nerd pagado

Respuestas:

234

La expansión de llaves , {x..y} se realiza antes de otras expansiones, por lo que no puede usarla para secuencias de longitud variable.

En su lugar, use el seq 2 $maxmétodo como lo indicó el usuario mob .

Entonces, para su ejemplo sería:

max=10
for i in `seq 2 $max`
do
    echo "$i"
done
whatsisname
fuente
No hay una buena razón para usar un comando externo, como seqcontar e incrementar números en el bucle for, por lo tanto, se recomienda evitar usarlo seq. Este comando se deja para compatibilidad con bash antiguo. Los comandos integrados son lo suficientemente rápidos. for (( EXP1; EXP2; EXP3 )) ...
Miller
13
@miller la for (( ... ))sintaxis no es POSIX y eso significa, por ejemplo, que no funcionará de forma inmediata en cosas como Alpine Linux. seqhace.
Wernight
@Wernight No solo Alpine Linux; #!/bin/shes Dash en Debian / Ubuntu.
Franklin Yu
72

Pruebe la versión de expresión aritmética de for:

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

Está disponible en la mayoría de las versiones de bash y también debe ser compatible con Bourne shell (sh).

PAUSA del sistema
fuente
1
@Flow: Hm, acabo de probarlo en un par de sistemas (basados ​​en Linux y BSD) con #! / Bin / sh y funcionó bien. Invocado bajo bash y específicamente bajo / bin / sh, aún funcionó. Tal vez la versión de sh importa? ¿Estás en un viejo Unix?
sistema PAUSE
8
Muy pocos sistemas tienen un dedicado sh, en cambio, lo convierten en un enlace a otro shell. Idealmente, se invoca un shell de este tipo shque solo admitiría esas características en el estándar POSIX, pero de manera predeterminada deja pasar algunas de sus características adicionales. El estilo for-loop de C no es una función POSIX, pero puede estar en shmodo por el shell real.
chepner
27

Paso el bucle manualmente:

i = 0
max = 10
mientras que [$ i -lt $ max]
hacer
    echo "salida: $ i"
    verdadero $ ((i ++))
hecho

Si no tiene que ser totalmente POSIX, puede usar la aritmética para el bucle:

max = 10
para ((i = 0; i <max; i ++)); hacer eco "salida: $ i"; hecho

O use jot (1) en sistemas BSD:

para i en $ (jot 0 10); hacer eco "salida: $ i"; hecho
Nietzche-jou
fuente
3
true $(( i++ ))no funciona en todos los casos, por lo que sería más portátil true $((i=i+1)).
Flujo
el punto y coma no debe aparecer "for ((i = 0; i <max; i ++));"
logan
9

Hay más de una forma de hacerlo.

max=10
for i in `eval "echo {2..$max}"`
do
    echo "$i"
done
efímero
fuente
77
Un riesgo de seguridad, ya evalque evaluará todo lo que establezca max. Considere max="2}; echo ha; #", luego reemplace echo hacon algo más destructivo.
chepner
66
(S) estableció el máximo en 10. Sin riesgo.
Conrad Meyer
9

Si el seqcomando está disponible en su sistema:

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

Si no, entonces use a los pobres seqcon perl:

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

Mira esas comillas.

multitud
fuente
Acabo de comprobar eso; no parece que lo sea
eykanal
5

Esta es una manera:
Bash:

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

La forma Bash anterior también funcionará para kshy zshcuando bash -cse reemplaza con ksh -co zsh -crespectivamente.

Nota: for i in {2..${max}}; do echo $i; donetrabaja en zshy ksh.

Jahid
fuente
4

Podemos iterar en bucle como en la programación en C.

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

Aquí funcionó en Mac OS X.

Incluye el ejemplo de una fecha BSD, cómo aumentar y disminuir la fecha también:

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

Bueno, como no tenía el seqcomando instalado en mi sistema (Mac OS X v10.6.1 (Snow Leopard)), terminé usando un whilebucle en su lugar:

max=5
i=1

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

* Se encoge de hombros * Lo que sea que funcione.

eykanal
fuente
2
seq es relativamente nuevo. Solo me enteré hace unos meses. Pero se puede utilizar un 'para' bucle !! La desventaja de un 'while' es que debe recordar incrementar el contador en algún lugar dentro del bucle, o de lo contrario bucle hacia abajo.
PAUSE del sistema
1

Todos estos hacen {1..8}y deberían ser POSIX. Tampoco se romperán si pones un condicional continueen el bucle. La forma canónica:

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

De otra manera:

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

y otro:

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

fuente
1

Utilizar:

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

Necesita la llamada explícita 'eval' para reevaluar el {} después de la sustitución de variables.

Chris Dodd
fuente