Bu

Bucles en Bash

{one: "1 ejercicio", many: "%{count} de ejercicios", other: "%{count} ejercicios"}

Acerca de Bucles

Bash tiene dos formas de hacer bucles:

  • Repetir algunos comandos para cada elemento de una lista de elementos,
  • Repetir algunos comandos mientras alguna condición sea verdadera (o falsa).

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.

Bucle for aritmético

Bash 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

Cuándo no usar for

No leas líneas con 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.

  1. La sustitución de comandos no está entrecomillada, así que está sujeta a la división de palabras. La división de palabras, por defecto, divide por espacios en blanco, no solo por saltos de línea. Este bucle for está iterando sobre las palabras del archivo, no sobre las líneas.
  2. La sustitución de comandos no está entrecomillada, así que está sujeta a la expansión de nombres de archivo. Cada palabra del archivo se hará coincidir con un patrón glob.

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.

Bucles 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».

Control de los bucles

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.

Leer las líneas de un archivo

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.

Until

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 ...

Bucles infinitos

A veces quieres repetir para siempre. Aquí tienes dos formas de hacerlo.

  1. un bucle while con una condición que siempre tiene un estado de salida exitoso

    while true; do ...
    
  2. un bucle for aritmético con expresiones vacías

    for ((;;)); do ...
    

Do-While

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
Editar en GitHub El enlace se abre en una ventana o pestaña nueva