Pipes: conectando comandos com |

1. O que é um Pipe e como funciona

O operador | (pipe) no Bash é um dos mecanismos mais poderosos e elegantes do Unix/Linux. Ele permite conectar a saída padrão (stdout) de um comando diretamente à entrada padrão (stdin) de outro comando, criando um fluxo contínuo de processamento de dados.

A metáfora do encanamento (pipe em inglês) é perfeita: assim como canos transportam água de um ponto a outro, os pipes no terminal transportam dados entre comandos. Cada comando no pipeline processa os dados recebidos e os passa adiante, criando uma cadeia de transformações.

O fluxo básico funciona da seguinte forma:

comando1 | comando2

Neste caso, tudo que comando1 escreve na saída padrão é automaticamente redirecionado para a entrada padrão de comando2, sem a necessidade de arquivos temporários.

2. Sintaxe básica e exemplos simples

A estrutura fundamental de um pipe é simples e direta:

comando1 | comando2

Exemplo clássico — listar apenas arquivos .txt no diretório atual:

ls -l | grep .txt

Aqui, ls -l lista todos os arquivos, e grep .txt filtra apenas as linhas que contêm ".txt".

Encadeamento de múltiplos pipes — você pode conectar quantos comandos desejar:

cat /var/log/syslog | grep "error" | head -10

Este comando:
1. Lê o arquivo de log (cat)
2. Filtra linhas com "error" (grep)
3. Mostra apenas as 10 primeiras ocorrências (head)

Exemplo prático — contar quantos usuários únicos estão logados:

who | cut -d' ' -f1 | sort | uniq | wc -l

3. Combinando Pipes com Redirecionamento

É importante entender a diferença entre | (pipe) e > (redirecionamento):
- | conecta a saída de um comando à entrada de outro
- > redireciona a saída para um arquivo

Uso simultâneo — é perfeitamente possível combinar ambos:

ps aux | grep apache > processos_apache.txt

Aqui, o pipe envia a saída de ps aux para grep apache, e o redirecionamento > salva o resultado filtrado em um arquivo.

Redirecionamento de stderr com pipes — por padrão, o pipe captura apenas stdout. Para incluir stderr:

comando 2>&1 | filtro

Ou de forma mais concisa no Bash 4+:

comando |& filtro

Exemplo prático:

find /proc -name "status" 2>&1 | grep -v "Permission denied" | head -5

4. Comandos frequentemente usados com Pipes

Alguns comandos são especialmente úteis em pipelines:

grep — filtra linhas que correspondem a um padrão:

dmesg | grep -i "usb"

sort — ordena as linhas recebidas:

cat notas.txt | sort -n

uniq — remove linhas duplicadas consecutivas (geralmente usado após sort):

cat palavras.txt | sort | uniq

wc — conta linhas, palavras e caracteres:

ls -1 | wc -l

Combinando tudo — um pipeline completo:

cat access.log | grep "404" | awk '{print $1}' | sort | uniq -c | sort -rn | head -10

Este comando analisa um log de acesso web, encontra erros 404, extrai IPs, conta ocorrências únicas e mostra os 10 IPs mais frequentes.

5. Pipes e Variáveis Especiais

Capturando saída com $() e pipes internos:

total_linhas=$(cat arquivo.txt | wc -l)
echo "Total de linhas: $total_linhas"

Uso de $? após cadeias de pipes — o status de saída de um pipeline é o do último comando executado:

grep "erro" log.txt | wc -l
echo "Status de saída: $?"

Para verificar o status de todos os comandos no pipeline:

set -o pipefail
grep "erro" log.txt | wc -l
echo "Status (pipefail): $?"

Diferença entre |, && e ||:
- |: executa comandos em paralelo, conectando saída/entrada
- &&: executa o próximo comando apenas se o anterior teve sucesso (exit code 0)
- ||: executa o próximo comando apenas se o anterior falhou

# Pipe: sempre executa
ls /tmp | grep arquivo

# AND: executa apenas se ls for bem-sucedido
ls /tmp && echo "Diretório existe"

# OR: executa apenas se ls falhar
ls /tmp || echo "Diretório não encontrado"

6. Técnicas Avançadas com Pipes

Pipe para xargs — converte stdin em argumentos de linha de comando:

find . -name "*.log" | xargs rm -f

Isso encontra todos os arquivos .log e os remove. Cuidado com nomes contendo espaços:

find . -name "*.log" -print0 | xargs -0 rm -f

Pipe para tee — divide o fluxo, enviando para a tela e para um arquivo simultaneamente:

ls -la | tee lista_completa.txt | grep ".sh"

Isso salva toda a listagem em lista_completa.txt e mostra apenas arquivos .sh na tela.

Named pipes (FIFOs) — pipes que existem como arquivos no sistema de arquivos:

# Criar um named pipe
mkfifo meu_pipe

# Em um terminal:
cat > meu_pipe

# Em outro terminal:
cat < meu_pipe

Named pipes são úteis para comunicação entre processos independentes ou scripts separados.

7. Erros Comuns e Boas Práticas

Esquecer de escapar caracteres especiais:

# Erro: o asterisco será expandido pelo shell
grep erro arquivo* | wc -l

# Correto: escapar ou usar aspas
grep "erro" arquivo* | wc -l

Pipes com comandos que não leem stdin:

# Erro: rm ignora stdin
ls | rm

# Correto: usar xargs
ls | xargs rm

Useless Use of Cat (UUOC) — usar cat desnecessariamente:

# Ineficiente
cat arquivo.txt | grep "padrão"

# Eficiente
grep "padrão" arquivo.txt

Verificar permissões e caminhos antes do pipe:

# Pode falhar silenciosamente
cat /etc/sombra | grep root

# Melhor: verificar antes
[ -r /etc/sombra ] && cat /etc/sombra | grep root || echo "Sem permissão"

Boas práticas:
- Sempre teste pipelines com dados de exemplo
- Use set -o pipefail em scripts para detectar falhas intermediárias
- Prefira grep "padrão" arquivo a cat arquivo | grep "padrão"
- Documente pipelines complexos com comentários
- Considere o uso de $() para capturar saídas intermediárias

Referências