Bu

Bucles en Bash

{one: "1 ejercicio", many: "%{count} 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 una condición sea verdadera (o falsa).

Bucles for

Para recorrer una lista de elementos usamos el bucle for.

for varname in words; do COMMANDS; done

words se expande mediante la división de palabras y la expansión de nombres de archivo (que vimos en el concepto de comillas). Cada palabra se asigna a varname por turno y se ejecutan los COMMANDS.

Aquí tienes algunos ejemplos que muestran cuándo puede resultar ú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 en cada uno de los parámetros de un script o de una función.

    for arg in "$@"; do
        echo "argument: ${arg}"
    done
    
  • Ejecutar algún comando para cada palabra separada por espacios 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í que tiene un bucle aritmético. Resultará algo familiar para quienes conocen 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 con 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 a menudo: iterar sobre la salida de cat.

for line in $(cat some.file); do 
    do_something_with "$line"
done

Esto está mal por dos motivos.

  1. La sustitución de comandos no está entre comillas, así que está sujeta a la división de palabras. La división de palabras, de forma predeterminada, divide por espacios en blanco, no solo por saltos de línea. Este bucle for recorre las palabras del archivo, no las líneas.
  2. La sustitución de comandos no está entre comillas, así que está sujeta a la expansión de nombres de archivo. Cada palabra del archivo se tratará como un patrón glob.

Como aprendimos en el concepto de comillas, la división de palabras se puede controlar con la variable IFS, y la expansión de nombres de archivo se puede desactivar. Pero suele ser una solución frágil. for no es el método adecuado para recorrer 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 se cumpla una condición.

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á si es «verdadero» o «falso».

Control de bucles

El comando break sale 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 ya se ha mencionado, 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 pasa como entrada al bucle con la redirección <.

  • El comando read devuelve «verdadero» si puede leer una línea completa de su entrada. Cuando la entrada se agota, o si la última línea no termina en un salto de línea, read devuelve «falso».

    • La forma realmente idiomática de leer las líneas de un archivo, incluso si la última línea no termina en 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 un carácter normal.

  • IFS= asigna el string vacío a IFS solo mientras dura el comando read. Esto desactiva temporalmente la división de palabras para conservar los espacios en blanco que haya al principio o al final de la línea de texto entrante.

until

La construcción until repite una secuencia de comandos mientras una condición es falsa. El bucle se repite hasta que la condición se cumple.

until CONDITION_COMMANDS; do COMMANDS; done

Es poco habitual usar until. Lo más común es usar un bucle «while not».

while ! CONDITION_COMANDS; do ...

Bucles infinitos

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

  1. un bucle while con una condición cuyo estado de salida siempre sea de éxito

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

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

Do-while

No existe una construcción do-while explícita, pero puedes conseguir el mismo efecto:

while true; do
    COMMANDS

    if END_CONDITION; then
        break
    fi
done
Editar en GitHub El enlace se abre en una nueva ventana o pestaña