Bash tiene dos formas de hacer bucles:
for
Para iterar sobre una lista de elementos, usamos el bucle for.
for varname in words; do COMMANDS; done
words se expande usando la división de palabras y la expansión de nombres de archivo (que aprendimos en el concepto de Quoting).
Cada palabra se asigna a varname por turno, y los COMMANDS se ejecutan.
Aquí tienes algunos ejemplos que muestran cuándo puede ser útil el bucle for
Ejecutar algunos comandos sobre un grupo de archivos.
# source all the bash library files in the current directory
for file in ./*.bash; do
source "$file"
done
Ejecutar algún comando sobre cada uno de los parámetros de un script o una función.
for arg in "$@"; do
echo "argument: ${arg}"
done
Ejecutar algún comando para cada palabra separada por espacios en blanco en la salida de un comando.
for i in $(seq 10); do
echo "${i} squared is $((i * i))"
done
Más adelante veremos algunas advertencias sobre este estilo.
for aritméticoBash sí tiene un bucle aritmético. Esto resultará algo familiar si vienes de otros lenguajes de programación.
for ((INITIALIZATION; CONDITION; INCREMENT)); do COMMANDS; done
Los paréntesis dobles en bash representan un contexto aritmético (veremos más sobre la aritmética de bash en un concepto posterior).
El ejemplo anterior usando seq se puede escribir así
for ((i = 1; i <= 10; i++)); do
echo "${i} squared is $((i * i))"
done
for
Este es un antipatrón que verás con frecuencia: iterar sobre la salida de cat.
for line in $(cat some.file); do
do_something_with "$line"
done
Esto está mal por 2 razones.
for está iterando sobre las palabras del archivo, no sobre las líneas.Como aprendimos en el concepto de Quoting, la división de palabras se puede controlar con la variable IFS, y la expansión de nombres de archivo se puede desactivar.
Pero esto tiende a ser una solución frágil.
for es el método equivocado para iterar sobre las líneas de un archivo.
La forma idiomática de leer un archivo es con un bucle while.
while
Usa while para repetir una secuencia de comandos mientras alguna condición sea verdadera.
while CONDITION_COMMANDS; do COMMANDS; done
Al igual que con if (como aprendimos en el concepto de Condicionales, no hay una sintaxis especial para CONDITION_COMMANDS.
El estado de salida de la lista de comandos determinará «verdadero» o «falso».
El comando break salta fuera del bucle.
El control continúa con el comando que sigue al terminador done del bucle.
El comando continue salta a la siguiente iteración del bucle.
Como se mencionó, un bucle while es la forma idiomática de leer un archivo.
while IFS= read -r line; do
do_something_with "$line"
done < some.file
El contenido del archivo se proporciona como entrada al bucle con la redirección <.
El comando read devuelve «verdadero» si puede leer una línea completa de su entrada.
Cuando se agota la entrada, o si la última línea termina sin un salto de línea final, entonces read devuelve «falso».
La forma realmente idiomática de leer las líneas de un archivo, incluso si la última línea no termina con un salto de línea, es
while IFS= read -r line || [[ -n "$line" ]]; do ...
La opción -r le dice a read que no sustituya secuencias de barra invertida: una barra invertida es solo un carácter normal.
IFS= asigna la cadena vacía a IFS solo durante la ejecución del comando read.
Esto desactiva temporalmente la división de palabras para que se conserve cualquier espacio en blanco al principio o al final de la línea de texto entrante.
La construcción until repite una secuencia de comandos mientras alguna condición sea falsa.
El bucle se repite hasta que la condición se vuelve verdadera.
until CONDITION_COMMANDS; do COMMANDS; done
Es raro usar until.
Es más común usar un bucle «while not».
while ! CONDITION_COMANDS; do ...
A veces quieres repetir para siempre. Aquí tienes dos formas de hacerlo.
un bucle while con una condición que siempre tiene un estado de salida exitoso
while true; do ...
un bucle for aritmético con expresiones vacías
for ((;;)); do ...
No existe una construcción explícita do-while, pero puedes lograr el mismo efecto:
while true; do
COMMANDS
if END_CONDITION; then
break
fi
done