Todo lo que necesita saber sobre Bash para bucles en Linux

Los scripts de bash son un medio altamente eficiente de automatizar tareas, particularmente aquellas que aprovechan otros programas existentes. Esta automatización a menudo requiere que se repita una operación similar varias veces, que es precisamente donde el bucle for entra en juego.

Los administradores de sistemas de Linux y Mac suelen estar familiarizados con las secuencias de comandos a través de la terminal, pero incluso los usuarios de Windows pueden participar en la acción con el Subsistema de Windows para Linux .

Cómo funcionan los scripts de Bash

Un script bash es simplemente un archivo de texto sin formato que contiene una serie de comandos que el shell bash puede leer y ejecutar. Bash es el shell predeterminado en macOS anteriores a Catalina y en la mayoría de las distribuciones de Linux.

Si nunca antes ha trabajado con un script de shell, debe comenzar con el caso más simple. Esto le permitirá practicar conceptos clave, incluida la creación del script y su ejecución.

Primero, cree el siguiente archivo en una ubicación conveniente (idealmente, abra una terminal y navegue al directorio deseado primero):

 #!/bin/bash
echo "Hello, World"

La primera línea le dice a todo lo que ejecute este programa cómo ejecutarlo (es decir, usando el intérprete de bash). El segundo es solo un comando como cualquier otro que pueda ingresar en la línea de comando. Guarde ese archivo como hello_world.sh , luego:

 $ chmod +x hello_world.sh
$ ./hello_world.sh

El comando chmod en la primera línea hace que el archivo sea ejecutable, lo que significa que se puede ejecutar escribiendo su nombre, como en la segunda línea.

Si ve las palabras "Hola, mundo" impresas en una línea en su terminal, entonces todo está funcionando como se requiere.

Cómo funcionan los bucles For

En la programación general, hay dos tipos principales de bucle for: numérico y foreach . El tipo numérico es tradicionalmente el más común, pero en el uso de bash suele ser al revés.

Los bucles for numéricos suelen centrarse en un único entero que determina cuántas iteraciones se llevarán a cabo, por ejemplo:

 for (i = 0; i < 100; i++) {
/* statements to execute repeatedly */
}

Este es un bucle for de apariencia familiar que iterará exactamente 100 veces, a menos que i se modifique dentro del bucle, u otra declaración haga que se detenga la ejecución del bucle for.

Los bucles Foreach, por el contrario, tienden a operar en estructuras como listas o matrices, e iteran para cada elemento dentro de esa colección:

 people = [ "Peter", "Paul", "Mary" ]
foreach (people as person) {
if (person == "Paul") {
...
}
}

Algunos lenguajes usan una sintaxis ligeramente diferente que cambia el orden de colección y artículo:

 people = [ "Peter", "Paul", "Mary" ]
for (person in people) {
if (person == "Paul") {
...
}
}

Para en bucles

En bash, el foreach (o for in ) es más común. La sintaxis básica es, simplemente:

 for arg in [list]
do
/* statements to execute repeatedly */
/* the value of arg can be obtained using $arg */
done

Por ejemplo, para recorrer en iteración tres archivos con nombres explícitos:

 for file in one.c two.c three.c
do
ls "$file"
done

Si tales archivos existen en el directorio actual, la salida de este script será:

 one.c
two.c
three.c

En lugar de un conjunto fijo de archivos, la lista se puede obtener mediante un patrón global (uno que incluye comodines, caracteres especiales que representan otros caracteres). En el siguiente ejemplo, el ciclo for itera en todos los archivos (en el directorio actual) cuyos nombres terminan en ".xml":

 for file in *.xml
do
ls -l "$file"
done

Aquí hay un resultado de ejemplo:

 $ -rw-r--r-- 1 bobby staff 2436 3 Nov 2019 feed.xml
$ -rw-r--r-- 1 bobby staff 6447 27 Oct 16:24 sitemap.xml

Esto puede parecerse mucho a una forma larga de hacer:

 $ ls -l *.xml

Pero hay una diferencia significativa: el bucle for ejecuta el programa ls 2 veces por separado, con un solo nombre de archivo que se le pasa cada vez. En el ejemplo de ls separado, el patrón glob (* .xml) primero coincide con los nombres de archivo y luego los envía todos, como parámetros de línea de comandos individuales, a una instancia de ls .

Aquí hay un ejemplo que usa el programa wc (recuento de palabras) para hacer la diferencia más obvia:

 $ wc -l *.xml
44 feed.xml
231 sitemap.xml
275 total

El programa wc cuenta el número de líneas en cada archivo por separado, luego imprime un recuento total en todos ellos. Por el contrario, si wc opera dentro de un bucle for:

 for file in *.xml
do
wc -l $file
done

Seguirá viendo el recuento de cada archivo:

 44 feed.xml
231 sitemap.xml

Pero no hay un total de resumen general porque wc se ejecuta de forma aislada, cada vez que el ciclo se repite.

Cuando una lista no es una lista

Hay un error muy fácil y común cuando se trata de bucles for, debido a la forma en que bash maneja los argumentos / cadenas entre comillas. Recorrer una lista de archivos debe hacerse así:

 for file in one.c two.c

Así no:

 for file in "one.c two.c"

El segundo ejemplo incluye nombres de archivos entre comillas dobles, lo que da como resultado una lista con un solo parámetro; el ciclo for solo se ejecutará una vez. Este problema se puede evitar utilizando una variable en tales casos:

 FILES="one.c two.c"
for file in $FILES
do
...
done

Tenga en cuenta que la declaración de la variable en sí necesita incluir su valor entre comillas dobles.

Porque sin lista

Sin nada para iterar, un bucle for opera en los argumentos de la línea de comandos que se proporcionaron al script cuando se invoca. Por ejemplo, si tiene un script llamado args.sh que contiene lo siguiente:

 #!/bin/sh
for a
do
echo $a
done

Luego, ejecutar args.sh le dará lo siguiente:

 $ ./args.sh one two three
one
two
three

Bash reconoce este caso y trata a do como el equivalente de for a en $ @ do donde $ @ es una variable especial que representa los argumentos de la línea de comandos.

Emulación de un bucle for numérico tradicional

Los scripts bash a menudo tratan con listas de archivos o líneas de salida de otros comandos, por lo que el tipo de bucle for in es común. Sin embargo, la operación tradicional de estilo C todavía es compatible:

 for (( i=1; i<=5; i++ ))
do
echo $i
done

Esta es la forma clásica con tres partes en las que:

  1. una variable se inicializa (i = 1) cuando el bucle se encuentra por primera vez
  2. el ciclo continúa mientras la condición (i <= 5) sea verdadera
  3. cada vez alrededor del ciclo, la variable se incrementa (i ++)

Iterar entre dos valores es un requisito lo suficientemente común como para que haya una alternativa más corta y un poco menos confusa:

 for i in {1..5}
do
echo $i
done

La expansión de la abrazadera que se lleva a cabo traduce efectivamente el bucle for anterior en:

 for i in 1 2 3 4

Control de bucle más fino con interrupción y continuación

Los bucles for más complejos a menudo necesitan una forma de salir temprano o reiniciar inmediatamente el bucle principal con el siguiente valor a su vez. Para hacerlo, bash toma prestadas las declaraciones break y continue que son comunes en otros lenguajes de programación. Aquí hay un ejemplo que usa ambos para encontrar el primer archivo que tiene más de 100 caracteres:

 #!/bin/bash
for file in *
do
if [ ! -f "$file" ]
then
echo "$file is not a file"
continue
fi
num_chars=$(wc -c < "$file")
echo $file is "$num_chars characters long"
if [ $num_chars -gt 100 ]
then
echo "Found $file"
break
fi
done

El bucle for aquí opera en todos los archivos del directorio actual. Si el archivo no es un archivo normal (por ejemplo, si es un directorio), la instrucción continue se usa para reiniciar el ciclo con el siguiente archivo sucesivamente. Si es un archivo normal, el segundo bloque condicional determinará si contiene más de 100 caracteres. Si es así, la instrucción break se usa para dejar inmediatamente el bucle for (y llegar al final del script).

Conclusión

Un script bash es un archivo que contiene un conjunto de instrucciones que se pueden ejecutar. Un bucle for permite que parte de un script se repita muchas veces. Con el uso de variables, comandos externos y las sentencias break y continue, los scripts bash pueden aplicar una lógica más compleja y realizar una amplia gama de tareas.