Substituição de processo
Aula sobre substituição de processo no Bash, uma técnica poderosa que permite passar a saída de um comando como se fosse um arquivo. Abordaremos a sintaxe <(comando), comparação de saídas, casos de uso práticos e exemplos detalhados, com exercícios para fixação.
A substituição de processo é um recurso avançado do Bash que permite tratar a saída de um comando como se fosse um arquivo temporário. Isso é extremamente útil quando você precisa passar a saída de um comando para outro que espera um arquivo como argumento, sem criar arquivos temporários no disco. A sintaxe básica é <(comando), que gera um descritor de arquivo virtual (geralmente um pipe ou um arquivo em /dev/fd) que pode ser lido pelo comando que o recebe.
Nesta aula, vamos explorar como usar a substituição de processo para comparar saídas de comandos, entender seus casos de uso mais comuns e ver exemplos práticos que demonstram seu poder. Ao final, você será capaz de integrar essa técnica em seus scripts para torná-los mais eficientes e elegantes.
<(comando)
A sintaxe <(comando) é chamada de substituição de processo. Quando o Bash encontra essa expressão, ele executa o comando entre parênteses e conecta sua saída a um descritor de arquivo (geralmente um pipe ou um arquivo em /dev/fd). O resultado é um caminho de arquivo (como /dev/fd/63) que pode ser usado como argumento para outros comandos. Esse arquivo virtual contém a saída do comando, e pode ser lido uma única vez (como um pipe).
É importante notar que a substituição de processo é uma extensão do Bash, não disponível em shells POSIX estritamente (como o sh). Ela é especialmente útil quando você precisa passar a saída de um comando para outro que espera um arquivo, como diff, comm, grep, entre outros. Veja um exemplo simples:
# Exemplo: comparar a listagem de dois diretórios
diff <(ls dir1) <(ls dir2)
Nesse caso, o diff recebe dois "arquivos" que contêm as saídas de ls dir1 e ls dir2, respectivamente, e mostra as diferenças entre eles. Sem a substituição de processo, você precisaria criar arquivos temporários e depois removê-los.
Comparando saídas
Um dos usos mais frequentes da substituição de processo é comparar a saída de dois comandos. O comando diff é um exemplo clássico, mas também podemos usar comm, cmp e até mesmo grep para processar saídas como arquivos. A vantagem é que você não precisa armazenar a saída em arquivos temporários, evitando poluição do diretório e problemas de concorrência.
Vamos ver um exemplo com comm, que compara dois arquivos ordenados linha por linha e mostra as linhas exclusivas de cada um ou comuns a ambos. Para usar comm, os arquivos precisam estar ordenados, então podemos usar sort na substituição:
# Comparar listas de usuários em dois sistemas
comm -12 <(cut -d: -f1 /etc/passwd | sort) <(cut -d: -f1 /etc/group | sort)
Outro exemplo é usar cmp para verificar se duas saídas são idênticas, embora diff -q seja mais comum. A substituição de processo permite que você faça comparações sem criar arquivos intermediários, tornando seus scripts mais limpos e seguros.
Casos de uso
A substituição de processo é útil em várias situações:
- Comparar saídas de comandos: como vimos,
diff,comm,cmppodem receber saídas de comandos diretamente. - Processar múltiplos arquivos: quando um comando aceita vários arquivos de entrada, você pode fornecer saídas de outros comandos como se fossem arquivos.
- Evitar arquivos temporários: em scripts que precisam de arquivos temporários, a substituição de processo elimina a necessidade de criar e limpar esses arquivos, reduzindo erros e melhorando a legibilidade.
- Compor pipelines complexos: você pode combinar substituição de processo com pipes e redirecionamentos para criar fluxos de dados sofisticados.
- Em loops e processamento em lote: por exemplo, para processar cada linha de uma saída como argumento para um comando que espera um arquivo.
Um caso comum é usar while read para iterar sobre a saída de um comando, mas a substituição de processo pode ser usada quando você precisa que o comando que lê o arquivo seja executado de forma síncrona, como em comparações.
Exemplos
Vamos ver alguns exemplos práticos e comentados:
# Exemplo 1: Verificar se dois comandos produzem a mesma saída
if diff -q <(echo "olá") <(echo "olá") >/dev/null; then
echo "As saídas são iguais"
else
echo "As saídas são diferentes"
fi
# Exemplo 2: Processar a saída de um comando como arquivo de entrada para outro
# Contar quantas linhas da saída de 'ps' contêm 'bash'
grep -c bash <(ps aux)
# Exemplo 3: Usar substituição de processo com 'while read' para processar linhas
while IFS= read -r linha; do
echo "Linha: $linha"
done < <(ls -la)
# Exemplo 4: Comparar a saída de dois comandos com 'comm'
comm -3 <(printf "a\nb\nc\n" | sort) <(printf "b\nc\nd\n" | sort)
No Exemplo 3, observe o uso de < <(comando) para redirecionar a entrada do loop while para a saída do comando. Isso é uma combinação de redirecionamento e substituição de processo, muito útil.
É importante lembrar que a substituição de processo cria um pipe, portanto a saída do comando é consumida uma única vez. Se você precisar ler a saída múltiplas vezes, talvez seja melhor armazenar em uma variável ou arquivo temporário.
Boas práticas
- Use substituição de processo quando precisar passar a saída de um comando como arquivo, mas evite usá-la em excesso para não prejudicar a legibilidade.
- Combine com
diff,comme outros comandos que aceitam arquivos para criar comparações poderosas. - Em scripts portáveis, lembre-se de que a substituição de processo é específica do Bash; se precisar de portabilidade, considere usar arquivos temporários com
trappara limpeza. - Tenha cuidado com o consumo de memória e descritores de arquivo: cada substituição de processo usa um descritor, e muitos podem esgotar o limite.
Referências
- Bash Reference Manual: Process Substitution
- Advanced Bash-Scripting Guide: Process Substitution
- GNU Coreutils: diff invocation
- Linux man page: comm
- GNU Grep Manual
- Bash Manual (GNU)
Exercícios
- Escreva um comando que use substituição de processo para comparar a saída de
ls -lem dois diretórios diferentes comdiffe exiba somente as linhas que diferem (sem contexto).✓ Resposta:diff --suppress-common-lines <(ls -l dir1) <(ls -l dir2) - Usando substituição de processo, conte quantas linhas da saída de
ps auxcontêm a palavra "root" (usegrep).✓ Resposta:grep -c root <(ps aux) - Crie um script que use
while readpara ler a saída defind . -name "*.txt"e imprima cada arquivo encontrado, usando substituição de processo.✓ Resposta:while IFS= read -r arquivo; do echo "$arquivo" done < <(find . -name "*.txt") - Explique por que a substituição de processo não é adequada se você precisar ler a saída do comando mais de uma vez, e sugira uma alternativa.
✓ Resposta: A substituição de processo usa um pipe, que é consumido após a leitura. Se precisar ler a saída várias vezes, armazene em uma variável ou em um arquivo temporário. Por exemplo:
saida=$(ls dir1); echo "$saida"; echo "$saida". - Implemente um comando que use
commpara mostrar as linhas que estão presentes apenas na saída dels dir1(e não emls dir2), assumindo que as saídas são ordenadas.✓ Resposta:comm -23 <(ls dir1 | sort) <(ls dir2 | sort)