Ejecución en paralelo en Bash

Este es uno de los capítulos del tutorial Scripts en Bash. Encontrarás los enlaces a todos los de capítulos, al final de este artículo.

En ocasiones nos encontramos que tenemos que ejecutar varios comandos en paralelo, ya sea porque tenemos que ahorrar tiempo o por la razón que sea. ¿Es posible la ejecución de scripts en paralelo en Bash?¿Como se puede implementar?¿Que herramientas y mecanismos tenemos a nuestra disposición para hacerlo?

En este artículo te mostraré como puedes utilizar xargs para ejecutar comandos y scripts en paralelo y que opciones y posibilidades te ofrece.

Ejecución en paralelo en Bash

¡Claro! El procesamiento en paralelo en Bash se refiere a la ejecución simultánea de múltiples tareas o comandos en un script de Bash. Esto permite aprovechar al máximo los recursos disponibles en un sistema y puede acelerar considerablemente la ejecución de un programa o script.

Hay varias formas de lograr el procesamiento en paralelo en Bash. Una de las formas más comunes es utilizando subprocesos en segundo plano (background processes) y el comando wait para esperar a que todos los subprocesos terminen antes de continuar con el script principal.

Aquí hay un ejemplo simple que muestra cómo ejecutar múltiples comandos en paralelo usando subprocesos en segundo plano,

#!/bin/bash

# Comando 1 en segundo plano
comando1 &
pid1=$!

# Comando 2 en segundo plano
comando2 &
pid2=$!

# Esperar a que los subprocesos terminen
wait $pid1
wait $pid2

# Continuar con el script principal
echo "Todos los subprocesos han terminado."

En este ejemplo, los comandos comando1 y comando2 se ejecutan en segundo plano utilizando el símbolo & al final de cada comando. Después de iniciar cada comando, se guarda el ID del proceso en una variable (pid1 y pid2) utilizando el comando especial $!, que representa el ID del último proceso en segundo plano ejecutado.

Luego, se utiliza el comando wait para esperar a que cada proceso en segundo plano termine. El comando wait bloquea la ejecución del script principal hasta que todos los procesos especificados hayan finalizado.

Después de que los subprocesos terminen, el script principal continúa ejecutando cualquier código adicional.

Es importante tener en cuenta que el número de subprocesos en paralelo que se pueden ejecutar al mismo tiempo puede estar limitado por el sistema operativo y los recursos disponibles en el sistema. Además, el procesamiento en paralelo puede introducir complejidad adicional debido a posibles problemas de concurrencia, como la competencia por recursos compartidos. Por lo tanto, es importante diseñar y probar cuidadosamente el uso de procesamiento en paralelo en un script de Bash.

Un ejemplo

Supongamos que tienes una lista de URLs de sitios web y deseas extraer el título de cada página para realizar un análisis posterior. En lugar de procesar cada URL secuencialmente, puedes aprovechar el procesamiento en paralelo para acelerar el proceso. Aquí hay un ejemplo de cómo podrías hacerlo,

#!/bin/bash

# Lista de URLs
urls=(
  "https://www.ejemplo1.com"
  "https://www.ejemplo2.com"
  "https://www.ejemplo3.com"
  # Añadir más URLs según sea necesario
)

# Función para extraer el título de una página web
extract_title() {
  url="$1"
  title=$(curl -s "$url" | grep -o "<title>.*</title>" | sed -e 's/<[^>]*>//g')
  echo "Título de $url: $title"
}

# Recorrer la lista de URLs y ejecutar la función extract_title en segundo plano para cada una
for url in "${urls[@]}"; do
  extract_title "$url" &
done

# Esperar a que todos los subprocesos terminen
wait

echo "Proceso de extracción de títulos completado."

En este ejemplo, tenemos una matriz urls que contiene las URLs de los sitios web que queremos analizar. La función extract_title se encarga de obtener el título de una página web utilizando el comando curl para realizar la solicitud HTTP, grep para extraer la línea del título y sed para eliminar las etiquetas HTML.

Luego, utilizamos un bucle for para recorrer la lista de URLs y ejecutar la función extract_title en segundo plano para cada URL utilizando el símbolo &. Esto permite que cada extracción de título se ejecute en paralelo.

Finalmente, utilizamos el comando wait para esperar a que todos los subprocesos terminen antes de continuar con el script principal.

Este enfoque de procesamiento en paralelo puede ayudarte a acelerar el proceso de extracción de títulos de las páginas web, especialmente cuando tienes una lista grande de URLs para analizar.

Utilizando xargs

xargs es un comando en Unix/Linux que se utiliza para construir y ejecutar comandos a partir de la entrada estándar. Permite tomar los elementos de la entrada y pasarlos como argumentos a otro comando especificado.

Una de las características útiles de xargs es su capacidad para procesar en paralelo, lo que significa que puede ejecutar múltiples instancias de un comando de forma simultánea utilizando múltiples hilos o procesos. Esto puede acelerar la ejecución de tareas al aprovechar los recursos disponibles del sistema.

La opción -P de xargs se utiliza para especificar el número de procesos paralelos que se ejecutarán. Por ejemplo, si utilizas -P 4, xargs ejecutará hasta 4 instancias del comando de forma simultánea.

Un ejemplo simple de cómo utilizar xargs para ejecutar comandos en paralelo,

# Ejemplo 1: Imprimir números en paralelo
seq 1 10 | xargs -P 4 -I {} bash -c 'echo {}'

# Ejemplo 2: Ejecutar comandos en paralelo
cat urls.txt | xargs -P 4 -I {} bash -c 'curl -s {} > {}.html'

# Ejemplo 3: Procesamiento en paralelo con función personalizada
cat archivos.txt | xargs -P 4 -I {} bash -c 'mi_funcion {}'

En el primer ejemplo, utilizamos seq 1 10 para generar una secuencia de números del 1 al 10, y luego .xargs. se encarga de ejecutar el comando bash -c 'echo {}' en paralelo para imprimir cada número.

En el segundo ejemplo, suponemos que tenemos un archivo urls.txt que contiene una lista de URLs. Utilizamos cat urls.txt para leer las URLs, y luego xargs ejecuta bash -c 'curl -s {} > {}.html' para descargar cada URL y guardar el contenido en un archivo HTML con el nombre correspondiente.

En el tercer ejemplo, asumimos que tenemos un archivo archivos.txt con una lista de nombres de archivos. xargs lee los nombres de archivo y ejecuta bash -c 'mi_funcion {}' para procesar cada archivo utilizando una función personalizada llamada mi_funcion.

Utilizando xargs

En el ejemplo anterior, podemos utilizar xargs en lugar del bucle for para ejecutar los comandos en paralelo,

#!/bin/bash

# Lista de URLs
urls=(
  "https://www.ejemplo1.com"
  "https://www.ejemplo2.com"
  "https://www.ejemplo3.com"
  # Añadir más URLs según sea necesario
)

# Función para extraer el título de una página web
extract_title() {
  url="$1"
  title=$(curl -s "$url" | grep -o "<title>.*</title>" | sed -e 's/<[^>]*>//g')
  echo "Título de $url: $title"
}

# Exportar la función para que esté disponible para xargs
export -f extract_title

# Utilizar xargs para ejecutar la función extract_title en paralelo
printf "%s\n" "${urls[@]}" | xargs -I {} -P 4 bash -c 'extract_title "$@"' _ {}

echo "Proceso de extracción de títulos completado."

En este ejemplo, utilizamos printf junto con "${urls[@]}" para imprimir cada URL en una línea separada. Luego, redirigimos la salida de printf al comando xargs.

Con xargs, utilizamos la opción -I {} para especificar un marcador de posición {} que será reemplazado por cada URL en la ejecución del comando. La opción -P 4 indica que queremos ejecutar hasta 4 procesos en paralelo. Puedes ajustar el número según la cantidad de recursos disponibles y la capacidad de tu sistema.

El comando bash -c 'extract_title "$@"' _ {} se encarga de ejecutar la función extract_title para cada URL. El marcador de posición {} se reemplaza con la URL correspondiente.

Finalmente, el resultado se muestra en la salida estándar y se imprime un mensaje indicando que el proceso de extracción de títulos se ha completado.


Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *