xargs na Pratica: Convertendo Entrada Padrao em Argumentos de Comando
O Que Voce Vai Aprender
- Como usar o xargs corretamente para conectar comandos que nao leem do stdin (rm, mv, cp, etc.)
- Como evitar o desastre classico onde
find | xargs rmquebra em nomes de arquivo com espacos - Como usar
-I {},-ne-Ppara substituicao de placeholder, lotes e execucao paralela
Resumo Rapido (as regras praticas)
- Comece com
find ... -print0 | xargs -0 <comando>(o padrao seguro para espacos) - Use
-I {}quando precisar inserir argumentos no meio de um comando - Use
-P Nquando quiser execucao paralela - Use
-rpara evitar desastres com entrada vazia (extensao GNU)
Ambiente
- Linux (GNU findutils). O macOS vem com BSD xargs, que difere em algumas opcoes.
- Testado no Ubuntu 22.04 com GNU xargs (findutils) 4.8.x
- Para basicos de pipe e stdin/stdout, veja Pipes e Redirecionamento Basico
O Que E o xargs e Por Que Voce Precisa Dele?
xargs le da entrada padrao e constroi uma lista de argumentos para outro comando. Pipes so podem alimentar stdout para stdin, entao comandos que esperam nomes de arquivo como argumentos (como rm, mv, cp) nao funcionam se voce simplesmente redirecionar para eles.
# Errado: rm nao le nomes de arquivo do stdin (nada e deletado, ou erro) find . -name "*.tmp" | rm # Correto: xargs converte stdin em argumentos para rm find . -name "*.tmp" | xargs rm
Em uma frase: xargs "cola" o conteudo de um pipe no slot de argumento do proximo comando.
1. Uso Basico: Construindo Argumentos a Partir de um Pipe
$ echo "a b c" | xargs echo a b c
xargs divide o stdin em espacos/quebras de linha e alimenta o resultado para o proximo comando como argumentos.
1-1. O Combo Classico com find
$ find . -name "*.log" | xargs ls -lh
Passa cada arquivo que o find descobre como argumento para ls -lh.
1-2. Evitando ARG_MAX com Grandes Conjuntos de Arquivos
A expansao glob do shell (ls *.log) atinge o limite ARG_MAX, mas o xargs divide a chamada automaticamente.
# Funciona mesmo com centenas de milhares de arquivos -- sem "Argument list too long" $ find /var/log -name "*.gz" | xargs gzip -t
2. Por Que Voce Precisa de -print0 e -0
Se nomes de arquivo contiverem espacos, tabulacoes, quebras de linha ou aspas, o xargs padrao os dividira incorretamente. Esta e a maior fonte de desastres com xargs.
2-1. O Exemplo Perigoso
# Um arquivo chamado "my file.txt" e dividido pelo espaco e xargs tenta # deletar dois arquivos: "my" e "file.txt" $ find . -name "*.txt" | xargs rm
2-2. O Padrao Seguro: Par de Separadores NUL
$ find . -name "*.txt" -print0 | xargs -0 rm
find -print0: separa a saida com NUL (\0)xargs -0: trata NUL como o separador
NUL e o unico caractere que nao pode aparecer em um nome de arquivo, entao a divisao e garantida.
Ao combinar find e xargs, faca de -print0 / -0 um reflexo.
Isso sozinho previne 80% dos desastres.
2-3. O Mesmo se Aplica ao grep -l / grep -rl
# Perigoso $ grep -rl "TODO" . | xargs sed -i 's/TODO/DONE/g' # Seguro $ grep -rlZ "TODO" . | xargs -0 sed -i 's/TODO/DONE/g'
grep -Z tambem emite saida separada por NUL. Combine grep -rlZ com xargs -0.
3. O Placeholder -I {}: Inserir Argumentos em Qualquer Lugar
Por padrao, xargs adiciona argumentos no final. Use -I {} quando precisar deles no meio (ou em multiplas posicoes).
3-1. Por Que Adicionar no Final Nao E Suficiente
# Errado: mv espera "origem destino", mas adicionar no final quebra a ordem $ ls *.bak | xargs mv archive/ # torna-se: mv archive/ file1.bak file2.bak ... (trata archive/ como origem)
3-2. Especifique a Posicao com -I {}
$ ls *.bak | xargs -I {} mv {} archive/
# expande para:
# mv file1.bak archive/
# mv file2.bak archive/
# (uma execucao por linha de entrada){} e substituido por cada linha de entrada individualmente.
3-3. Multiplas Substituicoes Tambem Funcionam
$ cat hosts.txt | xargs -I {} ssh {} "hostname && uptime"Com -I {}, xargs executa o comando uma vez por linha de entrada (sem lotes). Combine com -n / -P para grandes entradas.
4. Controlando Tamanho do Lote e Paralelismo com -n / -P
4-1. -n N: Passar N Argumentos por Vez
$ seq 1 10 | xargs -n 3 echo 1 2 3 4 5 6 7 8 9 10
echo e invocado uma vez por grupo de tres.
4-2. -P N: Executar N Processos em Paralelo
# Verificar arquivos .gz com 4 workers paralelos $ find . -name "*.gz" -print0 | xargs -0 -n 1 -P 4 gzip -t
-n 1: um item por invocacao-P 4: ate 4 processos simultaneos
-P 0 significa "tantos quanto o numero de nucleos da CPU" (extensao GNU). Uma forma simples de acelerar trabalho limitado por CPU.
Notas sobre execucao paralela:
- A ordem da saida se torna nao-deterministica (linhas de log de workers paralelos podem se intercalar)
- Cuidado com limites de conexao em trabalhos limitados por DB ou I/O
- A recuperacao de falhas e mais dificil, entao sempre comece com um dry-run em uma amostra pequena
5. Seguranca: -r / -t / -p
5-1. -r: Pular Execucao com Entrada Vazia (quase sempre necessario)
# Perigoso: rm pode ser chamado mesmo se find nao encontrou nada $ find . -name "nonexistent" | xargs rm # Seguro: nao faz nada com entrada vazia $ find . -name "nonexistent" | xargs -r rm
Pior caso: find ... | xargs rm -rf onde find retorna zero correspondencias e rm -rf acaba olhando para . e catastrofico. Faca de -r um reflexo.
GNU xargs passa entrada vazia por padrao, o que --no-run-if-empty (-r) suprime. BSD xargs (macOS) pula entrada vazia por padrao, entao ser explicito com -r melhora a portabilidade.
5-2. -t: Imprimir o Comando Antes de Executa-lo
$ find . -name "*.log" | xargs -t rm rm ./access.log ./error.log
-t ecoa o comando montado no stderr antes da execucao. Ele ainda executa o comando -- use o truque do echo abaixo para um dry-run real.
5-3. O Truque do echo: Um Dry-Run Real
# Imprima o que seria executado sem realmente executar $ find . -name "*.tmp" -print0 | xargs -0 echo rm rm ./a.tmp ./b.tmp ./c.tmp
Prefixar com echo transforma o comando montado em mera saida. Remova o echo quando estiver confiante.
5-4. -p: Confirmar Cada Invocacao Interativamente
$ find . -name "*.tmp" -print0 | xargs -0 -p rm rm ./a.tmp ./b.tmp ?...y
Executa apenas quando voce digita y. Util para operacoes sensiveis.
6. xargs vs find -exec: Quando Usar Qual?
find sozinho pode fazer coisas semelhantes com -exec. Qual escolher?
| Aspecto | find -exec |
xargs |
|---|---|---|
| Performance | Cria um processo por arquivo (lento) | Criacao em lote (rapido) |
Terminador + |
-exec ... + habilita lotes |
Lotes por padrao |
| Seguranca de nomes | Seguro por padrao (sem NUL necessario) | Requer -print0 / -0 |
| Execucao paralela | Nao suportada | Suportada via -P |
| Stdin arbitrario | Nao (apenas find) | Sim (saida de qualquer comando) |
| Legibilidade | One-liner auto-contido | Pipe e explicito |
6-1. Se o find Sozinho E Suficiente, Use -exec
# Simples e seguro
$ find . -name "*.tmp" -exec rm {} +-exec ... + faz lotes internamente e lida com espacos corretamente sem -print0.
6-2. Escolha xargs Quando
- Voce precisa de entrada de algo alem do
find(ex.:grep -l,ls,cat list.txt) - Voce quer execucao paralela (
-P) para ganho de velocidade - Voce esta combinando multiplos comandos em um pipeline
Regra geral: use find -exec ... + para fluxos apenas com find; recorra ao xargs para paralelo ou entrada que nao vem do find.
7. Modelos Praticos
Modelos seguros (copiar e colar)
# 1. Deletar muitos arquivos (seguro para espacos)
find /tmp -name "*.cache" -mtime +7 -print0 | xargs -0 -r rm
# 2. Mover arquivos de uma lista (placeholder)
cat filelist.txt | xargs -I {} mv {} /archive/
# 3. Comprimir arquivos em paralelo (4 workers)
find . -name "*.log" -print0 | xargs -0 -n 1 -P 4 gzip
# 4. Substituicao de texto em massa em arquivos
grep -rlZ "old-string" ./src | xargs -0 sed -i 's/old-string/new-string/g'
# 5. SSH paralelo para multiplos hosts
cat hosts.txt | xargs -I {} -P 8 ssh {} "uptime"
# 6. Dry-run antes de executar de verdade
find . -name "*.bak" -print0 | xargs -0 echo rmNao faca isso
find | xargs rmsem-print0/-0- Pular
-re arriscar uma chamada destrutiva com entrada vazia - Executar um novo comando
-Pparalelo diretamente em producao (teste pequeno primeiro) - Usar
-Pquando a ordem da saida importa (workers paralelos intercalam)