Eu tenho um monte de imagens PNG em um diretório. Eu tenho um aplicativo chamado pngout que executo para compactar essas imagens. Este aplicativo é chamado por um script que fiz. O problema é que esse script faz uma de cada vez, algo assim:
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
Processar apenas um arquivo por vez leva muito tempo. Depois de executar este aplicativo, vejo que a CPU está apenas 10%. Então descobri que posso dividir esses arquivos em 4 lotes, colocar cada lote em um diretório e disparar 4, de quatro janelas de terminal, quatro processos, então tenho quatro instâncias do meu script, ao mesmo tempo, processando essas imagens e o o trabalho leva 1/4 do tempo.
O segundo problema é que perdi tempo dividindo as imagens e lotes e copiando o script para quatro diretórios, abri 4 janelas de terminal, bla bla...
Como fazer isso com um script, sem precisar dividir nada?
Quero dizer duas coisas: primeiro, como faço para, a partir de um script bash, disparar um processo para segundo plano? (basta adicionar & no final?) Segundo: como faço para parar de enviar tarefas para segundo plano após enviar as quartas tarefas e colocar o script para esperar até que as tarefas terminem? Quer dizer, apenas enviar uma nova tarefa para segundo plano quando uma tarefa termina, mantendo sempre 4 tarefas em paralelo? se eu não fizer isso, o loop disparará zilhões de tarefas em segundo plano e a CPU ficará obstruída.
Responder1
Se você tiver uma cópia xargs
que suporte execução paralela com -P
, você pode simplesmente fazer
printf '%s\0' *.png | xargs -0 -I {} -P 4 ./pngout -s0 {} R{}
Para outras ideias, o wiki Wooledge Bash tem umseçãono artigo Gerenciamento de processos descrevendo exatamente o que você deseja.
Responder2
Além das soluções já propostas, você pode criar um makefile que descreve como fazer um arquivo compactado a partir de um descompactado, e usar make -j 4
para executar 4 jobs em paralelo. O problema é que você precisará nomear arquivos compactados e descompactados de maneira diferente ou armazená-los em diretórios diferentes; caso contrário, será impossível escrever uma regra make razoável.
Responder3
Se você possui GNU Paralelohttp://www.gnu.org/software/parallel/instalado você pode fazer isso:
parallel ./pngout -s0 {} R{} ::: *.png
Você pode instalar o GNU Parallel simplesmente:
wget http://git.savannah.gnu.org/cgit/parallel.git/plain/src/parallel
chmod 755 parallel
cp parallel sem
Assista aos vídeos de introdução do GNU Parallel para saber mais: https://www.youtube.com/playlist?list=PL284C9FF2488BC6D1
Responder4
Para responder às suas duas perguntas:
- sim, adicionar & no final da linha instruirá seu shell a iniciar um processo em segundo plano.
- usando o
wait
comando, você pode pedir ao shell para aguardar a conclusão de todos os processos em segundo plano antes de prosseguir.
Aqui está o script modificado para j
ser usado para controlar o número de processos em segundo plano. Quando NB_CONCURRENT_PROCESSES
for atingido, o script será redefinido j
para 0 e aguardará a conclusão de todos os processos em segundo plano antes de retomar sua execução.
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