GNU parallel: Executando Jobs em Paralelo pelo Shell
O Que Voce Vai Aprender
- Executar muitos comandos simultaneamente entre nucleos de CPU para reduzir o tempo total
- Evitar a saida intercalada e fora de ordem que voce obtem com
xargs -P - Usar
--jobloge--resumepara retomar de onde parou, pulando jobs concluidos
Resumo Rapido
- Muitas tarefas pesadas e independentes (converter, baixar, testar) ->
parallel - Precisa de saida limpa / ordem de entrada preservada ->
-k - Quer retomar uma execucao interrompida ->
--joblog+--resume
Premissas (ambiente alvo)
- Ubuntu / familia Debian (os conceitos se aplicam a outras distros tambem)
- Este e o GNU parallel (por Ole Tange). O
paralleldiferente fornecido emmoreutilsnao e compativel
O que e GNU parallel?
Conclusao: Ele recebe uma lista (de stdin ou argumentos de linha de comando) e executa um comando em cada item em paralelo. Por padrao, executa um job por nucleo de CPU.
GNU parallel pega a ideia do xargs de "construir um comando a partir de uma lista" e a especializa para execucao paralela e saida limpa. Duas formas basicas:
# 1) argumentos apos ::: parallel echo ::: a b c # 2) da entrada padrao seq 1 3 | parallel echo
a b c
Cada um de a, b, c lanca echo como um processo separado ao mesmo tempo. A concorrencia padrao e a quantidade de nucleos de CPU, entao mais entradas que nucleos sao alimentadas nos slots conforme eles ficam livres.
Por padrao, um comando e executado por item de entrada (o oposto do xargs). Para empacotar varios argumentos em uma invocacao, use -N (coberto abaixo).
Como instalar e executar o primeiro comando?
Conclusao: Instale com
apt install parallel. No primeiro uso, ele imprime um aviso de citacao; executeparallel --citationuma vez para registrar seu reconhecimento.
sudo apt update sudo apt install parallel parallel --version
GNU parallel pede para ser citado em trabalhos academicos e imprime uma solicitacao de citacao no primeiro uso. Se isso atrapalha scripts ou CI, execute o seguinte uma vez para registrar o reconhecimento (cria ~/.parallel/will-cite e silencia o aviso).
parallel --citation
Em algumas distros o pacote moreutils fornece um parallel diferente. Sempre verifique se a primeira linha de parallel --version diz GNU parallel. Se nao disser, nao e a ferramenta GNU.
Por que usar parallel em vez de xargs?
Conclusao:
xargs -Ptambem pode executar em paralelo, mas sua saida intercala linha por linha. parallel agrupa a saida por job e pode manter a ordem de entrada com-k.
xargs -P 4 e pratico, mas a saida padrao de jobs concorrentes tende a intercalar uma linha por vez. parallel armazena a saida de cada job internamente e a emite como um todo quando o job termina, entao nunca mistura.
| Aspecto | xargs -P |
parallel |
|---|---|---|
| Intercalacao de saida | Provavel | Agrupada por job |
| Saida na ordem de entrada | Nao garantida | Garantida com -k |
| Poder de placeholders | Apenas {} |
{} {.} {/} etc. |
| Log de execucao / resume | Nenhum | --joblog --resume |
| Exibicao de progresso | Nenhuma | --bar --eta |
Para velocidade bruta sozinha, xargs -P geralmente e suficiente. parallel ganha seu espaco quando voce nao pode corromper a saida ou quer retomar.
Como os placeholders funcionam?
Conclusao:
{}e a propria entrada.{.}remove a extensao,{/}e o nome base,{//}o diretorio,{#}o numero do job. Eles sao essenciais para construir nomes de saida.
Placeholders dizem ao parallel onde inserir cada entrada. Se voce os omitir, um {} e adicionado ao final.
# Converter cada *.wav para um .mp3 de mesmo nome ({.} remove a extensao)
parallel ffmpeg -i {} {.}.mp3 ::: *.wavOs principais placeholders:
| Sintaxe | Significado | Exemplo (entrada dir/file.txt) |
|---|---|---|
{} |
A propria entrada | dir/file.txt |
{.} |
Extensao removida | dir/file |
{/} |
Nome base (diretorio removido) | file.txt |
{//} |
Parte do diretorio | dir |
{/.} |
Nome base sem extensao | file |
{#} |
Numero sequencial do job | 1, 2, ... |
{%} |
Numero do slot do job | 1..(ate a concorrencia) |
# Prefixar cada entrada com seu numero de job
parallel 'echo job {#}: {}' ::: alpha beta gammajob 1: alpha job 2: beta job 3: gamma
Como controlar a quantidade de jobs e a ordem da saida?
Conclusao: Defina a concorrencia com
-j.-j0executa o maximo possivel,-j 200%e o dobro da quantidade de nucleos. Adicione-kpara emitir saida na ordem de entrada.
# 4 jobs por vez
parallel -j 4 ./convert.sh ::: *.dat
# Dobro da quantidade de nucleos (bom para trabalho limitado por I/O)
parallel -j 200% curl -O ::: "${urls[@]}"
# Sem limite de concorrencia (use com cuidado)
parallel -j0 echo ::: {1..100}Com execucao paralela, a saida chega na ordem de conclusao, quebrando o mapeamento entrada-saida. Para emitir na ordem de entrada, adicione -k (--keep-order).
seq 1 5 | parallel -k 'sleep $((RANDOM % 3)); echo {}'1 2 3 4 5
Antes de ir para producao, adicione --dry-run para imprimir apenas as linhas de comando que o parallel executaria. Detecte erros de expansao de placeholder aqui.
Como combinar multiplas entradas?
Conclusao: Multiplos
:::produzem o produto cartesiano.--linkemparelha itens por posicao. Para linhas multi-coluna, use--colsepe referencie{1}{2}.
# Produto cartesiano: a-1 a-2 b-1 b-2 c-1 c-2 (6 jobs) parallel echo ::: a b c ::: 1 2
# --link: emparelhar por posicao -> a-1 b-2 c-3 parallel --link echo ::: a b c ::: 1 2 3
Para ler entradas de um arquivo, use :::: (ou -a). Para dividir colunas como um CSV, use --colsep.
# Usar cada linha de hosts.txt como argumento
parallel ping -c1 {} :::: hosts.txt
# Dividir "user,host" em colunas
parallel --colsep ',' ssh {2} -l {1} uptime :::: targets.csvComo mostrar progresso, log e reexecutar falhas?
Conclusao:
--barmostra uma barra de progresso,--joblogregistra o resultado de cada job. Adicione--resumepara levar apenas os jobs nao concluidos para a proxima execucao.
# Mostrar uma barra de progresso
parallel --bar ./task.sh ::: {1..50}
# Registrar um log de execucao (codigo de saida e duracao por job)
parallel --joblog run.log ./task.sh ::: *.datCom um --joblog registrado, --resume pula jobs que ja tiveram sucesso e continua. Para tentar novamente apenas os que falharam, use --resume-failed.
# Apos uma interrupcao/falha, adicione --resume ao mesmo comando parallel --joblog run.log --resume ./task.sh ::: *.dat
Para parar cedo em um erro, use --halt.
# Parar apos uma falha, deixando jobs em execucao terminarem parallel --halt now,fail=1 ./task.sh ::: *.dat
--resume assume o mesmo arquivo --joblog e o mesmo comando. Mudar o comando ou as entradas quebra a retomada correta.
O que e o modo --pipe para dividir stdin?
Conclusao:
--pipedivide o proprio fluxo de entrada padrao em blocos e alimenta cada bloco para um comando paralelo. E adequado para agregar logs enormes.
Ate agora paralelizamos uma lista de argumentos. --pipe em vez disso divide um unico fluxo de entrada para processamento paralelo.
# Dividir um arquivo enorme em blocos de 10MB e grep cada um em paralelo cat huge.log | parallel --pipe --block 10M grep ERROR
--block define o tamanho de cada bloco. parallel divide nos limites de quebra de linha para que linhas nunca sejam cortadas ao meio.
Receitas praticas
Conclusao: Conversao em massa de imagens, downloads em massa e execucao de um comando em muitos hosts sao os classicos. Confirme com
--dry-runantes de ir para producao.
Templates para copiar e colar
# Redimensionar imagens em massa (saida nomeada {.}_small.jpg)
parallel convert {} -resize 50% {.}_small.jpg ::: *.jpg
# Baixar uma lista de URLs com 8 jobs
parallel -j8 wget -q ::: $(cat urls.txt)
# Mesmo comando em muitos hosts (saida agrupada por host)
parallel -k --tag ssh {} 'uptime' :::: hosts.txt
# Visualizar primeiro; remova --dry-run quando parecer correto
parallel --dry-run ./batch.sh {} ::: input/*Adicionar --tag prefixa cada linha de saida com sua entrada (como o nome do host), facilitando identificar qual job produziu qual resultado.