Tengo un montón de imágenes PNG en un directorio. Tengo una aplicación llamada pngout que ejecuto para comprimir estas imágenes. Esta aplicación se llama mediante un script que hice. El problema es que este script hace uno a la vez, algo como esto:
FILES=(./*.png)
for f in "${FILES[@]}"
do
echo "Processing $f file..."
# take action on each file. $f store current file name
./pngout -s0 $f R${f/\.\//}
done
Procesar un solo archivo a la vez lleva mucho tiempo. Después de ejecutar esta aplicación, veo que la CPU es solo del 10%. Entonces descubrí que puedo dividir estos archivos en 4 lotes, colocar cada lote en un directorio y activar 4, desde cuatro ventanas de terminal, cuatro procesos, de modo que tengo cuatro instancias de mi script, al mismo tiempo, procesando esas imágenes y el El trabajo toma 1/4 del tiempo.
El segundo problema es que perdí tiempo dividiendo las imágenes y lotes y copiando el script en cuatro directorios, abrí 4 ventanas de terminal, bla bla...
¿Cómo hacer eso con un guión, sin tener que dividir nada?
Me refiero a dos cosas: primero, ¿cómo puedo, desde un script bash, iniciar un proceso en segundo plano? (¿simplemente agregue & al final?) Segundo: ¿cómo dejo de enviar tareas a segundo plano después de enviar la cuarta tarea y hago que el script espere hasta que finalicen las tareas? Quiero decir, ¿simplemente enviar una nueva tarea a un segundo plano cuando finaliza una de ellas, manteniendo siempre 4 tareas en paralelo? si no lo hago, el bucle disparará millones de tareas en segundo plano y la CPU se obstruirá.
Respuesta1
Si tiene una copia xargs
que admita la ejecución paralela -P
, simplemente puede hacer
printf '%s\0' *.png | xargs -0 -I {} -P 4 ./pngout -s0 {} R{}
Para otras ideas, la wiki de Woolledge Bash tiene unasecciónen el artículo Gestión de procesos que describe exactamente lo que desea.
Respuesta2
Además de las soluciones ya propuestas, puede crear un archivo MAKE que describa cómo crear un archivo comprimido a partir de un archivo sin comprimir y utilizarlo make -j 4
para ejecutar 4 trabajos en paralelo. El problema es que necesitará nombrar los archivos comprimidos y sin comprimir de manera diferente, o almacenarlos en directorios diferentes; de lo contrario, será imposible escribir una regla de creación razonable.
Respuesta3
Si tienes GNU Paralelohttp://www.gnu.org/software/parallel/instalado puedes hacer esto:
parallel ./pngout -s0 {} R{} ::: *.png
Puede instalar GNU Parallel simplemente mediante:
wget http://git.savannah.gnu.org/cgit/parallel.git/plain/src/parallel
chmod 755 parallel
cp parallel sem
Mire los vídeos de introducción de GNU Parallel para obtener más información: https://www.youtube.com/playlist?list=PL284C9FF2488BC6D1
Respuesta4
Para responder a tus dos preguntas:
- sí, agregar & al final de la línea le indicará al shell que inicie un proceso en segundo plano.
- Con el
wait
comando, puede pedirle al shell que espere a que finalicen todos los procesos en segundo plano antes de continuar.
Aquí está el script modificado para que j
se utilice para realizar un seguimiento del número de procesos en segundo plano. Cuando NB_CONCURRENT_PROCESSES
se alcance, el script se restablecerá j
a 0 y esperará a que finalicen todos los procesos en segundo plano antes de reanudar su ejecución.
files=(./*.png)
nb_concurrent_processes=4
j=0
for f in "${files[@]}"
do
echo "Processing $f file..."
# take action on each file. $f store current file name
./pngout -s0 "$f" R"${f/\.\//}" &
((++j == nb_concurrent_processes)) && { j=0; wait; }
done