Aprende scripts de bash con multi-threading con GNU Parallel

Si estás cansado de que tus scripts de Bash tardan una eternidad en ejecutarse, este tutorial es para ti. A menudo, puedes ejecutar scripts de Bash en paralelo, lo que puede acelerar drásticamente el resultado. ¿Cómo? ¡Usando la utilidad GNU Parallel, también llamada simplemente Parallel, con algunos ejemplos útiles de GNU Parallel!

Parallel ejecuta scripts de Bash en paralelo a través de un concepto llamado multiprocesamiento. Esta utilidad te permite ejecutar diferentes trabajos por CPU en lugar de solo uno, reduciendo el tiempo de ejecución de un script.

En este tutorial, vas a aprender a ejecutar scripts de Bash en multiprocesamiento con un montón de excelentes ejemplos de GNU Parallel!

Prerrequisitos

Este tutorial estará lleno de demostraciones prácticas. Si planeas seguirlo, asegúrate de tener lo siguiente:

  • A Linux computer. Any distribution will work. The tutorial uses Ubuntu 20.04 running on Windows Subsystem for Linux (WSL).
  • Sesión iniciada con un usuario con privilegios de sudo.

Instalación de GNU Parallel

Para empezar a acelerar scripts de Bash con multiprocesamiento, primero debes instalar Parallel. Así que comencemos descargándolo e instalándolo.

1. Abre un terminal de Bash.

2. Ejecuta wget para descargar el paquete Parallel. El comando a continuación descarga la última versión (parallel-latest) en el directorio de trabajo actual.

wget https://ftp.gnu.org/gnu/parallel/parallel-latest.tar.bz2

Si prefieres utilizar una versión anterior de GNU Parallel, puedes encontrar todos los paquetes en el sitio de descarga oficial.

3. Ahora, ejecuta el comando tar a continuación para descomprimir el paquete que acabas de descargar.

A continuación, el comando utiliza la bandera x para extraer el archivo, j para especificar que se dirige a un archivo con extensión .bz2, y f para aceptar un archivo como entrada para el comando tar. sudo tar -xjf parallel-latest.tar.bz2

sudo tar -xjf parallel-latest.tar.bz2

Ahora deberías tener un directorio llamado parallel- con el mes, día y año del último lanzamiento.

4. Navega hasta la carpeta de archivo del paquete con cd. En este tutorial, la carpeta de archivo del paquete se llama parallel-20210422, como se muestra a continuación.

Navigating to the Parallel archive folder

5. A continuación, compila e instala el binario de GNU Parallel ejecutando los siguientes comandos:

./configure
 make
 make install

Ahora, verifica que Parallel se haya instalado correctamente verificando la versión instalada.

parallel --version
Checking the GNU Parallel version

Cuando ejecutes Parallel por primera vez, es posible que también veas un par de líneas alarmantes que muestren texto como perl: warning:. Esas advertencias indican que Parallel no puede detectar la configuración de tu idioma y localización actuales. Pero no te preocupes por esas advertencias por ahora. Aprenderás cómo solucionar esas advertencias más tarde.

Configuración de GNU Parallel

Ahora que Parallel está instalado, ¡puedes usarlo de inmediato! Pero primero, es importante configurar algunos ajustes menores antes de comenzar.

Mientras aún estés en tu terminal de Bash, acepta el permiso de investigación académica de GNU Parallel citando que lo mencionarás en cualquier investigación académica especificando el parámetro citation seguido de will cite.

Si no deseas apoyar a GNU o a sus mantenedores, no es necesario aceptar citar para usar GNU Parallel.

parallel --citation
will cite

Cambia la configuración regional estableciendo las siguientes variables de entorno ejecutando las líneas de código a continuación. Establecer variables de entorno de idioma y configuración regional como esta no es un requisito. Pero GNU Parallel las verifica cada vez que se ejecuta.

Si las variables de entorno no existen, Parallel se quejará de ellas cada vez, como viste en la sección anterior.

Este tutorial asume que eres un hablante de inglés. También se admiten otros idiomas.

export LC_ALL=C man
export LANGUAGE=en_US
export LANG=en_US.UTF-8
Setting locale and language for GNU Parallel

Ejecución de comandos de shell ad hoc.

¡Vamos a comenzar a usar GNU Parallel! Para empezar, aprenderás la sintaxis básica. Una vez que te sientas cómodo con la sintaxis, luego verás algunos ejemplos prácticos de GNU Parallel más adelante.

Para empezar, cubramos un ejemplo súper simple de simplemente imprimir los números del 1 al 5.

1. En tu terminal Bash, ejecuta los siguientes comandos. ¿Emocionante, verdad? Bash utiliza el comando echo para enviar los números del 1 al 5 a la terminal. Si colocaras cada uno de estos comandos en un script, Bash ejecutaría cada uno secuencialmente, esperando a que termine el anterior.

En este ejemplo, estás ejecutando cinco comandos que apenas llevan tiempo. Pero, ¿imagina si esos comandos fueran scripts de Bash que realmente hicieran algo útil pero tomaran una eternidad en ejecutarse?

 echo 1
 echo 2
 echo 3
 echo 4
 echo 5

Ahora, ejecuta cada uno de esos comandos al mismo tiempo con Parallel como se muestra a continuación. En este ejemplo, Parallel ejecuta el comando echo y, designado por los :::, pasa ese comando los argumentos, 1, 2, 3, 4, 5. Los tres dos puntos le indican a Parallel que estás proporcionando la entrada a través de la línea de comandos en lugar del conducto (más adelante).

En el siguiente ejemplo, pasaste un solo comando a Parallel sin opciones. Aquí, como en todos los ejemplos de Parallel, Parallel inició un nuevo proceso para cada comando utilizando un núcleo de CPU diferente.

# Desde la línea de comandos
 parallel echo ::: 1 2 3 4 5

Todos los comandos de Parallel siguen la sintaxis parallel [Opciones] <Comando para multihilo>.

3. Para demostrar la entrada paralela desde el pipeline de Bash, crea un archivo llamado count_file.txt como se muestra a continuación. Cada número representa el argumento que pasarás al comando echo.

 1
 2
 3
 4
 5

4. Ahora, ejecuta el comando cat para leer ese archivo y pasar la salida a Parallel, como se muestra a continuación. En este ejemplo, {} representa cada argumento (1-5) que se pasará a Parallel.

# Desde el pipeline: cat count_file.txt | parallel echo {}
GNU Parallel Example #1

Comparación entre Bash y GNU Parallel

En este momento, el uso de Parallel podría parecer simplemente una forma complicada de ejecutar comandos Bash. Pero el beneficio real para ti es el ahorro de tiempo. Recuerda, Bash se ejecutará solo en un núcleo de CPU, mientras que GNU Parallel se ejecutará en varios a la vez.

1. Para demostrar la diferencia entre los comandos secuenciales de Bash y Parallel, crea un script de Bash llamado test.sh con el siguiente código. Crea este script en el mismo directorio donde creaste el archivo count_file.txt anteriormente.

El script de Bash a continuación lee el archivo count_file.txt, espera durante 1, 2, 3, 4 y 5 segundos, muestra la duración del sueño en la terminal y termina.

#!/bin/bash
 nums=$(cat count_file.txt) # Leer count_file.txt
 for num in $nums           # Por cada línea en el archivo, iniciar un bucle
 do
     sleep $num             # Leer la línea y esperar esa cantidad de segundos
     echo $num              # Imprimir la línea
 done

2. Ahora, ejecuta el script usando el time comando para medir cuánto tiempo tarda el script en completarse. Tomará 15 segundos.

time ./test.sh

3. Ahora, usa el comando time nuevamente para realizar la misma tarea, pero esta vez utiliza Parallel para hacerlo.

El siguiente comando realiza la misma tarea, pero esta vez, en lugar de esperar a que se complete el primer bucle antes de comenzar el siguiente, se ejecutará en cada núcleo de la CPU y comenzará tantos como sea posible al mismo tiempo.

time cat count_file.txt | parallel "sleep {}; echo {}"
The prompt on the right side of the terminal confirms the time each command took to complete

¡Conoce la ejecución en seco!

Ahora es el momento de adentrarse en algunos ejemplos más del mundo real de GNU Parallel. Pero antes de hacerlo, debes conocer la bandera --dryrun. Esta bandera es útil cuando quieres ver qué sucederá sin que Parallel lo haga realmente.

La bandera --dryrun puede ser la última comprobación de cordura antes de ejecutar un comando que no se comporte como pensabas. Desafortunadamente, si ingresas un comando que podría dañar tu sistema, lo único que GNU Parallel te ayudará a hacer es dañarlo más rápido!

parallel --dryrun "rm rf {}"

Ejemplo de GNU Parallel #1: Descargar archivos desde la web

Para esta tarea, descargarás una lista de archivos desde varias URL en la web. Por ejemplo, estas URL podrían representar páginas web que deseas guardar, imágenes o incluso una lista de archivos de un servidor FTP.

Para este ejemplo, descargarás una lista de paquetes de archivos (y los archivos SIG) desde el servidor FTP de GNU Parallel.

1. Crea un archivo llamado download_items.txt, obtén algunos enlaces de descarga desde el sitio oficial de descargas y agrégales al archivo separados por una nueva línea.

 https://ftp.gnu.org/gnu/parallel/parallel-20120122.tar.bz2
 https://ftp.gnu.org/gnu/parallel/parallel-20120122.tar.bz2.sig
 https://ftp.gnu.org/gnu/parallel/parallel-20120222.tar.bz2
 https://ftp.gnu.org/gnu/parallel/parallel-20120222.tar.bz2.sig

Puedes ahorrar tiempo utilizando la biblioteca Beautiful Soup de Python para extraer todos los enlaces de la página de descargas.

2. Lee todas las URL desde el archivo download_items.txt y pásalas a Parallel, que invocará wget y pasará cada URL.

cat download_items.txt | parallel wget {}

No olvides que {} en un comando paralelo es un marcador de posición para la cadena de entrada!

3. Tal vez necesites controlar el número de hilos que GNU Parallel utiliza al mismo tiempo. Si es así, agrega el parámetro --jobs o -j al comando. El parámetro --jobs limita la cantidad de hilos que pueden ejecutarse simultáneamente al número que especifiques.

Por ejemplo, para limitar Parallel a descargar cinco URL al mismo tiempo, el comando se vería así:

#!/bin/bash
 cat download_items.txt | parallel --jobs 5 wget {}

El parámetro --jobs en el comando anterior se puede ajustar para descargar cualquier cantidad de archivos, siempre y cuando la computadora en la que estás ejecutando tenga suficientes CPUs para procesarlos.

4. Para demostrar el efecto del parámetro --jobs, ajusta ahora la cantidad de trabajos y ejecuta el comando time para medir cuánto tiempo lleva cada ejecución.

 time cat download_items.txt | parallel --jobs 5 wget {}
 time cat download_items.txt | parallel --jobs 10 wget {}

Ejemplo de GNU Parallel #2: Descomprimir paquetes de archivos

Ahora que tienes todos estos archivos de archivo descargados del ejemplo anterior, ahora debes descomprimirlos.

Mientras estás en el mismo directorio que los paquetes de archivo, ejecuta el siguiente comando de Parallel. Observa el uso del comodín (*). Dado que este directorio contiene tanto archivos de archivo y los archivos SIG, debes indicarle a Parallel que solo procese archivos .tar.bz2.

sudo parallel tar -xjf ::: *.tar.bz2

¡Bonus! Si estás utilizando GNU parallel de forma interactiva (no en un script), agrega la bandera --bar para que Parallel te muestre una barra de progreso mientras se ejecuta la tarea.

Showing the output from the --bar flag

Ejemplo de GNU Parallel #3: Eliminación de archivos

Si has seguido los ejemplos uno y dos, ahora deberías tener muchas carpetas en tu directorio de trabajo ocupando espacio. ¡Así que eliminemos todos esos archivos en paralelo!

Para eliminar todas las carpetas que comienzan con parallel- usando Parallel, lista todas las carpetas con ls -d y pasa cada una de esas rutas de carpeta a Parallel, invocando rm -rf en cada carpeta, como se muestra a continuación.

¡Recuerda la bandera --dryrun!

ls -d parallel-*/ | parallel "rm -rf {}"

Conclusión

Ahora puedes automatizar tareas con Bash y ahorrarte mucho tiempo. Lo que elijas hacer con ese tiempo depende de ti. Ya sea que ahorrar tiempo signifique salir un poco temprano del trabajo o leer otro post del blog ATA, es tiempo que recuperas en tu día.

Ahora piensa en todos los scripts de larga duración en tu entorno. ¿Cuáles puedes acelerar con Parallel?

Source:
https://adamtheautomator.com/how-to-speed-up-bash-scripts-with-multithreading-and-gnu-parallel/