Tengo un problema de scripting de shell en el que recibo un directorio lleno de archivos de entrada (cada archivo contiene muchas líneas de entrada), y necesito procesarlos individualmente, redirigiendo cada una de sus salidas a un archivo único (también conocido como file_1.input necesita para ser capturado en file_1.output, y así sucesivamente).
Antes del paralelo , simplemente iteraría sobre cada archivo en el directorio y realizaría mi comando, mientras realizaba algún tipo de técnica de temporizador / conteo para no abrumar a los procesadores (suponiendo que cada proceso tuviera un tiempo de ejecución constante). Sin embargo, sé que ese no siempre será el caso, por lo que usar una solución similar a "paralela" parece ser la mejor manera de obtener múltiples secuencias de comandos de shell sin escribir código personalizado.
Si bien he pensado en algunas formas de acelerar en paralelo para procesar cada uno de estos archivos (y permitirme administrar mis núcleos de manera eficiente), todos parecen extravagantes. Tengo lo que creo que es un caso de uso bastante fácil, por lo que preferiría mantenerlo lo más limpio posible (y nada en los ejemplos paralelos parece ser mi problema).
¡Cualquier ayuda sería apreciada!
ejemplo de directorio de entrada:
> ls -l input_files/
total 13355
location1.txt
location2.txt
location3.txt
location4.txt
location5.txt
Guión:
> cat proces_script.sh
#!/bin/sh
customScript -c 33 -I -file [inputFile] -a -v 55 > [outputFile]
Actualización : Después de leer la respuesta de Ole a continuación, pude juntar las piezas que faltaban para mi propia implementación paralela. Si bien su respuesta es excelente, aquí está mi investigación adicional y las notas que tomé:
En lugar de ejecutar mi proceso completo, pensé en comenzar con un comando de prueba de concepto para probar su solución en mi entorno. Vea mis dos implementaciones diferentes (y notas):
find /home/me/input_files -type f -name *.txt | parallel cat /home/me/input_files/{} '>' /home/me/output_files/{.}.out
Utiliza find (no ls, que puede causar problemas) para encontrar todos los archivos aplicables dentro de mi directorio de archivos de entrada y luego redirige sus contenidos a un directorio y archivo por separado. Mi problema desde arriba fue leer y redirigir (el guión real era simple), por lo que reemplazar el guión con gato fue una buena prueba de concepto.
parallel cat '>' /home/me/output_files/{.}.out ::: /home/me/input_files/*
Esta segunda solución utiliza el paradigma variable de entrada de paralelo para leer los archivos, sin embargo, para un novato, esto era mucho más confuso. Para mí, usar find a and pipe satisfizo mis necesidades perfectamente.
fuente
La forma estándar de hacer esto es configurar una cola y generar cualquier cantidad de trabajadores que sepan cómo extraer algo de la cola y procesarla. Puede usar un fifo (también conocido como canalización) para la comunicación entre estos procesos.
A continuación se muestra un ejemplo ingenuo para demostrar el concepto.
Un script de cola simple:
Y un trabajador:
process_file
podría definirse en algún lugar de su trabajador, y puede hacer lo que sea que necesite.Una vez que tenga esas dos piezas, puede tener un monitor simple que inicie el proceso de la cola y cualquier número de procesos de trabajo.
Supervisar script:
Ahí tienes. Si realmente hace esto, es mejor configurar el fifo en el monitor y pasar la ruta tanto a la cola como a los trabajadores, para que no estén acoplados ni pegados a una ubicación específica para el fifo. Lo configuré de esta manera en la respuesta específicamente para que quede claro lo que estás usando mientras lo lees.
fuente
monitor_workers
es comoprocess_file
, es una función que hace lo que quieras. Sobre el monitor, tenías razón; debe guardar las imágenes de sus trabajadores (para que pueda enviar una señal de muerte) y el contador debe incrementarse cuando se inicia un trabajador. He editado la respuesta para incluir eso.parallel
. Creo que es tu idea, totalmente implementada.Otro ejemplo:
Encontré los otros ejemplos innecesariamente complejos, cuando en la mayoría de los casos, lo anterior es lo que puede haber estado buscando.
fuente
Una herramienta comúnmente disponible que puede hacer paralelización es make. GNU make y algunos otros tienen la
-j
opción de realizar compilaciones paralelas.Ejecutar
make
así (supongo que sus nombres de archivo no contienen caracteres especiales,make
no es bueno con ellos):fuente
Esto es para realizar el mismo comando en un gran conjunto de archivos en el directorio actual:
Esto ejecuta el
customScript
en cadatxt
archivo, poniendo la salida enouttxt
archivos. Cambia según lo necesites. La clave para que esto funcione es el procesamiento de la señal, utilizando SIGUSR1 para que el proceso secundario pueda informar al proceso principal que se ha completado. El uso de SIGCHLD no funcionará, ya que la mayoría de las declaraciones en el script generarán señales de SIGCHLD para el script de shell. Intenté esto reemplazando su comando consleep 1
, el programa usó 0.28s de la CPU del usuario y 0.14s de la CPU del sistema; esto fue solo en unos 400 archivos.fuente
wait
suficientemente "inteligente"; pero volverá después de recibir laSIGUSR1
señal. El niño / trabajador envía un mensajeSIGUSR1
al padre, que se captura (trap
), y disminuye$worker
(trap
cláusula) y regresa de forma anormalwait
, permitiendo que laif [ $worker -lt $num_workers ]
cláusula se ejecute.O simplemente use
xargs -P
, no es necesario instalar un software adicional:Un poco de explicación para las opciones:
-I'XXX'
establece la cadena que se reemplazará en la plantilla de comando con el nombre del archivo-P4
ejecutará 4 procesos en paralelo-n1
colocará solo un archivo por ejecución aunque se encuentren dos XXX-print0
y-0
trabajar juntos, permitiéndole tener caracteres especiales (como espacios en blanco) en los nombres de archivofuente