Substituição de processo: <() e >()
1. Introdução à Substituição de Processo
A substituição de processo é um recurso poderoso do Bash que permite tratar a saída ou entrada de um comando como se fosse um arquivo. Introduzida no Korn Shell e posteriormente adotada pelo Bash, essa funcionalidade resolve limitações dos pipes tradicionais ao permitir que múltiplos fluxos de dados sejam combinados de forma elegante.
A diferença fundamental entre <() e >() é direta:
- <() fornece dados para um comando (funciona como entrada)
- >() recebe dados de um comando (funciona como saída)
Enquanto pipes conectam stdout de um comando ao stdin de outro, a substituição de processo cria descritores de arquivo temporários que podem ser usados em qualquer posição da linha de comando, inclusive como argumentos para comandos que esperam nomes de arquivo.
2. Sintaxe e Mecanismo Interno
A sintaxe básica é simples:
# Substituição como entrada
comando <(outro_comando)
# Substituição como saída
comando >(outro_comando)
Internamente, o shell cria um FIFO (pipe nomeado) ou utiliza /dev/fd para conectar o processo ao descritor de arquivo. Quando você escreve diff <(ls dir1) <(ls dir2), o Bash:
- Executa
ls dir1els dir2em subshells - Cria descritores de arquivo apontando para a saída desses processos
- Passa esses descritores como argumentos para o
diff
Em sistemas Linux modernos, o Bash usa /dev/fd quando disponível, que é mais eficiente que FIFOs. No macOS, o comportamento pode variar, mas geralmente funciona de forma equivalente.
O Bash, ksh e zsh suportam substituição de processo, mas o POSIX sh não. Isso significa que scripts usando essa feature não são portáveis para ambientes com apenas /bin/sh.
3. Substituição com <() – Alimentando Dados
O uso mais comum de <() é comparar saídas de comandos:
# Comparar listagens de diretórios
diff <(ls -l /etc) <(ls -l /etc.default)
# Comparar saída de processos em execução
diff <(ps aux) <(ps aux | grep -v grep)
# Combinar com grep para filtrar antes da comparação
diff <(grep "ERROR" log1.txt) <(grep "ERROR" log2.txt)
Outro padrão útil é processar saídas linha a linha:
while IFS= read -r linha
do
echo "Processando: $linha"
done < <(comando_que_gera_muitas_linhas)
Note o espaço entre < e <(comando). Isso é necessário para evitar ambiguidade com redirecionamento.
4. Substituição com >() – Capturando Saída
A substituição com >() permite redirecionar saída para processamento paralelo:
# Registrar saída em log enquanto exibe no terminal
comando > >(tee -a log.txt)
# Comprimir saída em tempo real
comando_verboso > >(gzip > saida.gz)
# Processar stderr separadamente
comando 2> >(grep "ERROR" > erros.log)
Exemplo prático de logging paralelo:
#!/bin/bash
executar_comando() {
echo "Iniciando processamento..."
for i in {1..5}; do
echo "Linha $i"
sleep 0.5
done
echo "Processamento concluído"
}
# Redireciona stdout para dois lugares simultaneamente
executar_comando > >(tee stdout.log) 2> >(tee stderr.log >&2)
5. Casos de Uso Avançados
Substituições aninhadas permitem construções complexas:
# Comparar saídas de comandos que usam substituição
diff <(comando1 <(comando2)) <(comando3)
# Exemplo real: comparar sorted lists
diff <(sort <(find /dir1 -type f)) <(sort <(find /dir2 -type f))
Combinando com exec para redirecionamento global:
#!/bin/bash
exec 3> >(gzip > saida.gz)
echo "Esta linha vai para o arquivo comprimido" >&3
echo "Esta também" >&3
exec 3>&- # Fecha o descritor
Em pipelines complexos:
# Processar múltiplos arquivos simultaneamente
while IFS= read -r arquivo; do
cat "$arquivo" | grep "padrão" | sort
done < <(find /dados -name "*.log" -mtime -7)
6. Limitações e Armadilhas Comuns
A principal armadilha com >() é a ordenação da saída. Como o processo de substituição executa em paralelo, a saída pode chegar ao terminal fora de ordem:
# Ordem imprevisível
echo "inicio" > >(sleep 1; cat)
echo "fim"
Outra limitação: substituição de processo não funciona em todos os contextos de redirecionamento. Por exemplo, não é possível usar:
# Isto NÃO funciona
< <(comando) > arquivo
Para portabilidade, lembre-se que scripts usando substituição de processo não funcionam em /bin/sh tradicional. Sempre declare #!/bin/bash no shebang.
7. Comparação com Alternativas
Pipes tradicionais:
# Pipe - funciona, mas limitado a um fluxo
ls dir1 | sort
# Substituição - permite múltiplos fluxos
diff <(ls dir1 | sort) <(ls dir2 | sort)
Arquivos temporários:
# Com arquivo temporário (mais verboso)
tmp1=$(mktemp)
tmp2=$(mktemp)
ls dir1 > "$tmp1"
ls dir2 > "$tmp2"
diff "$tmp1" "$tmp2"
rm "$tmp1" "$tmp2"
# Com substituição (mais limpo)
diff <(ls dir1) <(ls dir2)
A substituição de processo é geralmente mais legível e evita a limpeza manual de arquivos temporários. Para operações simples com um único fluxo, pipes são suficientes. Para múltiplos fluxos ou quando comandos esperam nomes de arquivo, a substituição é superior.
8. Exemplos Práticos e Boas Práticas
Script para comparar saídas em tempo real:
#!/bin/bash
# monitora_mudancas.sh - Compara saída de comando periodicamente
COMANDO="ps aux --sort=-%mem | head -10"
anterior=$(mktemp)
while true; do
atual=$(mktemp)
eval "$COMANDO" > "$atual"
diff "$anterior" "$atual" && echo "Sem mudanças" || echo "Mudanças detectadas"
mv "$atual" "$anterior"
sleep 5
done
Pipeline de processamento paralelo:
#!/bin/bash
# processa_logs.sh - Processa logs em paralelo
LOG_DIR="/var/log"
ARQUIVOS=$(find "$LOG_DIR" -name "*.log" -mtime -1)
# Processa múltiplos arquivos simultaneamente
while IFS= read -r arquivo; do
grep "ERROR" "$arquivo" > >(sort | uniq > "erros_$(basename $arquivo)")
done < <(echo "$ARQUIVOS")
Dicas de depuração:
# Verificar o que o shell está criando
echo <(date) # Mostra /dev/fd/63 ou similar
ls -l /dev/fd/ # Lista descritores ativos
# Testar se substituição está funcionando
diff <(echo "teste1") <(echo "teste2")
Referências
- GNU Bash Manual - Process Substitution — Documentação oficial do Bash sobre substituição de processo, com sintaxe detalhada e exemplos
- Advanced Bash-Scripting Guide - Process Substitution — Guia avançado com exemplos práticos e casos de uso complexos
- Bash Hackers Wiki - Process Substitution — Explicação técnica do mecanismo interno e diferenças entre shells
- Linux Journal - Process Substitution in Bash — Artigo prático com exemplos do mundo real e comparações com alternativas
- Stack Overflow - What is process substitution — Discussão comunitária com exemplos e esclarecimento de dúvidas comuns