Substituição de processo
Esta aula aborda a substituição de processo no Bash, um recurso avançado que permite tratar a saída de um comando como se fosse um arquivo. Você aprenderá a sintaxe <(comando), a comparar saídas de comandos diretamente, e verá casos de uso práticos com exemplos detalhados.
A substituição de processo é um recurso poderoso do Bash que permite passar a saída de um comando como se fosse um arquivo para outro comando. Isso é especialmente útil quando você precisa comparar saídas de dois comandos ou usar a saída de um comando como entrada para outro sem criar arquivos temporários. Compreender esse mecanismo amplia sua capacidade de escrever scripts eficientes e elegantes.
Diferente de um pipe simples, que conecta a saída de um comando à entrada de outro, a substituição de processo cria um descritor de arquivo virtual que pode ser referenciado como um caminho de arquivo. Isso permite que você utilize comandos que esperam argumentos de arquivo, como diff ou comm, diretamente com a saída de outros comandos.
<(comando)
A sintaxe fundamental da substituição de processo é <(comando). O Bash executa o comando dentro dos parênteses e substitui a expressão por um caminho de arquivo (como /dev/fd/63) que, quando lido, produz a saída do comando. Esse recurso é parte da especificação POSIX e está disponível em shells como Bash, Zsh e Ksh.
Por exemplo, imagine que você queira listar os arquivos de dois diretórios e compará-los com diff. Sem substituição de processo, você precisaria criar arquivos temporários:
ls dir1 > /tmp/lista1.txt
ls dir2 > /tmp/lista2.txt
diff /tmp/lista1.txt /tmp/lista2.txt
rm /tmp/lista1.txt /tmp/lista2.txtCom substituição de processo, você pode fazer isso em uma única linha, sem arquivos temporários:
diff <(ls dir1) <(ls dir2)O Bash expande <(ls dir1) para algo como /dev/fd/63, que é um pipe ligado à saída do comando ls dir1. O comando diff então lê desse descritor como se fosse um arquivo.
Comparando saídas
Um dos usos mais comuns da substituição de processo é comparar a saída de dois comandos. Ferramentas como diff, comm e cmp esperam argumentos de arquivo, e a substituição de processo permite fornecer a saída de comandos diretamente. Isso é especialmente útil para comparar listas de arquivos, conteúdo de variáveis ou resultados de consultas.
Por exemplo, para comparar as variáveis de ambiente de dois processos, você pode usar:
diff <(env | sort) <(env -i bash -c 'env' | sort)Outro exemplo: comparar a lista de pacotes instalados em dois sistemas (se você tiver acesso a eles) ou comparar a saída de dois comandos que geram listas ordenadas. O comando comm é particularmente útil para encontrar linhas em comum ou exclusivas entre dois arquivos ordenados:
comm -3 <(sort arquivo1.txt) <(sort arquivo2.txt)A substituição de processo também pode ser usada com diff para comparar a saída de um comando com um arquivo real, como em diff arquivo.txt <(comando).
Casos de uso
Além de comparações, a substituição de processo é útil em vários cenários:
- Processar saídas múltiplas: Você pode usar a substituição de processo para passar a saída de vários comandos para um único comando que aceita múltiplos arquivos, como
diffoupaste. - Evitar arquivos temporários: Em scripts, reduz a necessidade de criar e limpar arquivos temporários, tornando o código mais limpo e menos propenso a erros.
- Loop sobre saídas: Embora seja possível usar
while readcom um pipe, a substituição de processo pode ser mais conveniente quando você precisa preservar variáveis dentro de um loop (já que um pipe cria um subshell). Por exemplo:
while IFS= read -r linha; do
echo "Processando: $linha"
done < <(comando_que_gera_saida)Nesse caso, o loop é executado no shell atual, permitindo que variáveis modificadas dentro do loop persistam após o término.
- Testar saídas como argumentos: Alguns comandos, como
grep, aceitam múltiplos arquivos; você pode usar a substituição de processo para incluir a saída de um comando como um dos arquivos.
Exemplos
Vamos explorar alguns exemplos práticos para consolidar o entendimento.
Exemplo 1: Comparar a lista de arquivos de dois diretórios
diff <(ls -1 dir1) <(ls -1 dir2)Isso mostra as diferenças entre os nomes dos arquivos nos dois diretórios. A opção -1 do ls garante uma saída em uma coluna, facilitando a comparação linha a linha.
Exemplo 2: Usar comm para encontrar linhas comuns entre dois arquivos ordenados
comm -12 <(sort lista1.txt) <(sort lista2.txt)O comando comm -12 suprime as colunas 1 e 2, mostrando apenas as linhas comuns aos dois arquivos. Aqui, a substituição de processo permite ordenar os arquivos na hora, sem criar arquivos temporários ordenados.
Exemplo 3: Processar a saída de um comando em um loop sem subshell
total=0
while IFS= read -r num; do
total=$((total + num))
done < <(seq 1 10)
echo "Soma de 1 a 10: $total"Se você usasse um pipe (seq 1 10 | while ...), a variável total seria alterada em um subshell e a alteração seria perdida. Com a substituição de processo, o loop é executado no shell atual, e a variável total é atualizada corretamente.
Exemplo 4: Passar a saída de um comando como argumento para grep em múltiplos arquivos
grep -n "erro" <(dmesg) /var/log/syslogAqui, dmesg é executado e sua saída é tratada como um arquivo, permitindo que o grep pesquise a palavra "erro" tanto na saída de dmesg quanto no arquivo /var/log/syslog.
Boas práticas e observações finais
Ao usar a substituição de processo, lembre-se de que o comando dentro de <( ) é executado em um subshell, mas sua saída é disponibilizada como um arquivo. Isso pode ter implicações de desempenho se o comando for muito pesado, pois a saída pode ser armazenada em buffer. Em geral, é seguro e eficiente para a maioria dos casos.
Outra observação: a substituição de processo não está disponível em todos os shells. Se você escrever scripts para portabilidade, verifique se o shell alvo suporta esse recurso (Bash, Zsh e Ksh suportam).
Por fim, sempre que possível, prefira a substituição de processo a criar arquivos temporários, pois isso torna o script mais limpo e evita problemas com limpeza em caso de falhas.
Referências
- Bash Reference Manual: Process Substitution
- Advanced Bash-Scripting Guide: Process Substitution
- Greg's Wiki: Process Substitution
- GNU Coreutils: diff invocation
- Linux man page for comm
Exercícios
- Escreva um comando que use substituição de processo para comparar a saída de
ls -1 /etccom a saída dels -1 /usrusandodiff. - Explique por que o loop a seguir não atualiza a variável
countfora do loop, e como você corrigiria usando substituição de processo. - Use a substituição de processo com o comando
commpara exibir apenas as linhas exclusivas do primeiro arquivo (não presentes no segundo). Considere que os arquivosa.txteb.txtjá estão ordenados. - Escreva um comando que use substituição de processo para mostrar as linhas comuns entre a saída de
ps -ee o conteúdo de um arquivoprocessos.txt(que contém nomes de processos, um por linha). Dica: usegrep -f. - Crie um script que use substituição de processo para comparar a saída de
datecom a saída dedate -ue exiba uma mensagem indicando se são iguais ou diferentes. Usediffe verifique o código de saída.
diff <(ls -1 /etc) <(ls -1 /usr)count=0
seq 1 5 | while read n; do
count=$((count + n))
done
echo "Soma: $count"count=0
while read n; do
count=$((count + n))
done < <(seq 1 5)
echo "Soma: $count"comm -23 <(cat a.txt) <(cat b.txt) O -23 suprime as colunas 2 e 3, deixando apenas as linhas exclusivas do primeiro arquivo.grep -f processos.txt <(ps -e) Isso fará com que o grep use os padrões do arquivo para buscar na saída de ps -e.#!/bin/bash
if diff <(date) <(date -u) > /dev/null; then
echo "As saídas são iguais."
else
echo "As saídas são diferentes."
fi