Pipelines de transmissão e pipes nomeados para maior vazão
Use FIFOs e substituição de processos para transmitir dados entre etapas sem arquivos intermediários.
Pipelines de transmissão e pipes nomeados para maior vazão é uma aula grátis de DevOps Bootcamp no CoddyKit. Esta é a aula 4 de 4. Você pode ler a aula completa abaixo gratuitamente — depois pratica ao vivo no navegador com um editor de código integrado e um tutor de IA 24/7. Faz parte do caminho de aprendizado de DevOps Bootcamp, e seu progresso é sincronizado entre a web e o app CoddyKit. O curso de DevOps Bootcamp inclui 4 aulas no total.
Por que arquivos intermediários prejudicam a vazão
Ao encadear comandos como sort file.txt > tmp.txt && uniq tmp.txt > result.txt, você paga um custo oculto: gravações e leituras no disco, além de o fluxo de processamento ficar parado até que a primeira etapa seja concluída por completo antes de a próxima começar.
Os fluxos de processamento contínuos eliminam esse custo. Os dados passam diretamente do produtor para o consumidor na memória, etapa por etapa e de forma simultânea. Essa é a ideia central por trás dos pipes do Unix — e os pipes nomeados (FIFOs) ampliam esse conceito.
- Pipe anônimo (
|): conecta dois comandos adjacentes na mesma linha do shell. - Pipe nomeado (FIFO): um arquivo especial no sistema de arquivos que permite que processos não relacionados transmitam dados entre si.
- Substituição de processo: permite que um comando trate a saída de outro comando como se fosse um arquivo.
Esta lição mostra como aplicar os três recursos para maximizar a vazão em fluxos de trabalho reais do Bash.
Anatomia de um fluxo de processamento contínuo
Um pipe anônimo conecta a saída padrão de um processo à entrada padrão do processo seguinte. O kernel mantém os dois processos em execução simultaneamente em um buffer de memória de tamanho fixo (normalmente 64 KB no Linux).
A ideia principal é: o fluxo de processamento é tão rápido quanto sua etapa mais lenta. Se o produtor for mais rápido, ele ficará bloqueado quando o buffer estiver cheio. Se o consumidor for mais rápido, ficará bloqueado quando o buffer estiver vazio. Esse controle de fluxo por contrapressão é automático e não tem custo adicional.
O exemplo abaixo conta endereços IP exclusivos em um grande registro de acessos sem jamais gravar um arquivo temporário. Cada etapa é executada simultaneamente:
#!/usr/bin/env bash
# Stream a 2 GB access log — all stages run in parallel
grep '"GET' /var/log/nginx/access.log \
| awk '{print $1}' \
| sort \
| uniq -c \
| sort -rn \
| head -20Criando pipes nomeados com mkfifo
Um pipe nomeado (FIFO — First In, First Out) é criado com mkfifo. Ele aparece no sistema de arquivos como um arquivo comum, mas os dados gravados nele nunca são armazenados no disco — fluem diretamente para o processo que os lê.
Comportamentos importantes a lembrar:
- Uma gravação em um FIFO fica bloqueada até que um leitor o abra, e vice-versa.
- A entrada do FIFO permanece no sistema de arquivos; você precisa excluí-la com
rmquando terminar. - Vários escritores são permitidos, mas a ordem entre eles não é garantida.
Abaixo: um produtor compacta dados em um FIFO enquanto um consumidor os envia para o S3 simultaneamente — sem necessidade de arquivo temporário.
#!/usr/bin/env bash
mkfifo /tmp/stream_pipe
# Producer: compress in background
gzip -c /var/log/syslog > /tmp/stream_pipe &
# Consumer: read from FIFO (runs in foreground)
wc -l < /tmp/stream_pipe
wait
rm /tmp/stream_pipeSubstituição de processo: tratando um comando como arquivo
A substituição de processo usa a sintaxe <(command) ou >(command). O Bash cria um FIFO (ou um descritor de arquivo /dev/fd/N) nos bastidores e passa o caminho para o comando externo.
Isso é útil quando uma ferramenta espera um argumento de nome de arquivo em vez da entrada padrão. Sem a substituição de processo, seria necessário um arquivo temporário; com ela, os dados são transmitidos diretamente.
<(cmd)— o comando externo lê a saída de cmd.>(cmd)— o comando externo grava na entrada de cmd.
#!/usr/bin/env bash
# diff two sorted streams without creating temp files
diff <(sort /etc/passwd) <(sort /etc/group)
# Compare live command output against a baseline
diff <(ls /usr/bin | sort) <(cat ~/bin_baseline.txt | sort)Tee: dividindo um fluxo para vários consumidores
tee lê a entrada padrão e a grava tanto na saída padrão quanto em um ou mais arquivos. Combinado com a substituição de processo, ele permite ramificar um único fluxo para vários fluxos de processamento simultaneamente — tudo isso sem acessar o disco.
Esse padrão é útil quando, por exemplo, você quer registrar dados brutos e processá-los ao mesmo tempo.
#!/usr/bin/env bash
# Generate 100000 random numbers, then simultaneously:
# 1. compute the sum
# 2. find the maximum
# 3. count lines (saved to a variable)
seq 1 100000 \
| tee >(awk '{s+=$1} END{print "Sum:", s}') \
>(awk 'BEGIN{m=0} $1>m{m=$1} END{print "Max:", m}') \
| wc -l | xargs echo "Count:"Padrão de ramificação: um produtor, vários consumidores
Quando uma única fonte de dados precisa alimentar vários consumidores independentes, combine tee com várias substituições de processo >(). Cada consumidor recebe o fluxo completo e é executado simultaneamente.
Isso evita ler o arquivo de origem várias vezes. Para um arquivo de 10 GB, a diferença é enorme — uma leitura do disco em vez de N leituras.
#!/usr/bin/env bash
# Read a large CSV once; simultaneously:
# - count rows
# - extract column 2 to a file
# - pass column 3 to a stats script
cat large_data.csv \
| tee \
>(wc -l > /tmp/row_count.txt) \
>(cut -d',' -f2 > /tmp/col2.txt) \
>(cut -d',' -f3 | awk '{sum+=$1} END{print sum}' > /tmp/col3_sum.txt) \
> /dev/null
echo "Rows:" $(cat /tmp/row_count.txt)
echo "Col3 sum:" $(cat /tmp/col3_sum.txt)Padrão de agregação: vários produtores, um consumidor
O oposto da ramificação é a agregação: várias fontes independentes transmitindo dados para um único consumidor. Os FIFOs nomeados tornam isso simples.
Um caso de uso comum é mesclar em tempo real fluxos de registro de vários servidores ou agregar resultados parciais de trabalhadores paralelos.
Observe que, com vários escritores, o consumidor verá uma saída intercalada — isso é adequado para dados orientados por linhas, nos quais cada linha é independente, mas você deverá gerenciar a ordenação por conta própria se a sequência for importante.
#!/usr/bin/env bash
mkfifo /tmp/fanin_pipe
# Three producers write concurrently into the same FIFO
for host in web1 web2 web3; do
ssh "$host" 'tail -n 500 /var/log/app.log' > /tmp/fanin_pipe &
done
# Single consumer reads all merged output
grep 'ERROR' /tmp/fanin_pipe | sort | uniq -c | sort -rn
wait
rm /tmp/fanin_pipeUsando mkfifo para compactação paralela
Um dos usos mais práticos dos FIFOs é a compactação paralela. Ferramentas como pigz (gzip paralelo) ou pbzip2 leem um fluxo; você pode encaminhar os dados brutos diretamente, sem preparar um arquivo não compactado.
O padrão abaixo arquiva um diretório, compacta-o usando todos os núcleos da CPU e transmite o resultado para um host remoto — tudo simultaneamente:
#!/usr/bin/env bash
# Tar + parallel compress + stream to remote — no temp files
# Requires: pigz (parallel gzip)
tar cf - /data/large_dir \
| pigz -p 4 \
| ssh backup-host 'cat > /backups/large_dir.tar.gz'
# Verify the remote file exists
ssh backup-host 'ls -lh /backups/large_dir.tar.gz'Controlando o tamanho do buffer e o bloqueio
Os pipes têm um buffer do kernel (geralmente de 64 KB). Quando o buffer está cheio, o escritor fica bloqueado; quando está vazio, o leitor fica bloqueado. Normalmente, esse é o comportamento desejado, mas, em alguns casos, o bloqueio causa um deadlock.
Risco de deadlock: se o processo A grava no FIFO1 e lê do FIFO2, enquanto o processo B grava no FIFO2 e lê do FIFO1, ambos podem ficar bloqueados esperando que o outro consuma os dados primeiro.
Soluções:
- Coloque pelo menos um lado em segundo plano (
&) para que ele não bloqueie o shell. - Use
mbufferoupvpara adicionar um buffer maior na memória entre as etapas. - Use
pv -q -B 128mpara inserir um buffer de 128 MB, suavizando picos de vazão.
#!/usr/bin/env bash
# pv adds a 64 MB buffer and shows throughput
# Useful when producer and consumer have bursty speeds
dd if=/dev/urandom bs=1M count=200 \
| pv -B 64m \
| gzip \
| wc -cExemplo prático: agregador de registros em tempo real
Veja um padrão completo e realista: acompanhe vários arquivos de registro, mescle os fluxos por meio de um pipe nomeado, filtre os erros e grave um resumo em tempo real — tudo sem arquivos intermediários e com todas as etapas sendo executadas em paralelo.
Esse é o tipo de fluxo de processamento que seria executado como um script de monitoramento em segundo plano em um servidor de produção.
#!/usr/bin/env bash
FIFO=/tmp/log_aggregator
mkfifo "$FIFO"
cleanup() { rm -f "$FIFO"; }
trap cleanup EXIT INT TERM
# Fan-in: tail multiple logs into the FIFO
tail -F /var/log/syslog /var/log/auth.log > "$FIFO" &
TAIL_PID=$!
# Consumer: filter and timestamp errors in real time
grep --line-buffered -i 'error\|fail\|crit' "$FIFO" \
| while IFS= read -r line; do
printf '[%s] %s\n' "$(date '+%H:%M:%S')" "$line"
done
kill "$TAIL_PID" 2>/dev/nullComparando o desempenho de fluxos de processamento e arquivos temporários
Você pode medir a diferença real de vazão entre as abordagens de transmissão contínua e de arquivos temporários usando time. A abordagem com fluxo de processamento vence em grandes conjuntos de dados porque:
- As etapas são executadas simultaneamente — CPU e entrada e saída se sobrepõem.
- Não há entrada e saída de disco para dados intermediários — somente a saída final é gravada no disco.
- O uso de memória permanece constante, independentemente do tamanho da entrada (transmissão, não armazenamento em buffer).
Um teste simples para comparar as duas abordagens:
#!/usr/bin/env bash
# Approach 1: Temp file (sequential)
time bash -c '
seq 1 5000000 > /tmp/nums.txt
sort -n /tmp/nums.txt > /tmp/sorted.txt
uniq /tmp/sorted.txt | wc -l
rm /tmp/nums.txt /tmp/sorted.txt
'
echo '---'
# Approach 2: Streaming pipeline (concurrent)
time bash -c 'seq 1 5000000 | sort -n | uniq | wc -l'Verificação de conhecimento: comportamento de bloqueio de pipes nomeados
Considere o seguinte script:
mkfifo /tmp/mypipe
echo 'hello' > /tmp/mypipe
echo 'done'O que acontece quando esse script é executado sem processos em segundo plano nem leitores?
Recapitulação: pipelines de streaming e pipes nomeados
Nesta lição, explorou como mover dados de forma eficiente entre processos sem arquivos intermediários:
- Pipes anônimos (
|) conectam comandos adjacentes e executam todas as etapas simultaneamente, com controle automático de fluxo. - Pipes nomeados (
mkfifo) criam uma entrada FIFO no sistema de arquivos que permite a processos não relacionados ou em segundo plano transmitir dados entre si — as gravações ficam bloqueadas até que haja um leitor. - Substituição de processos (
<(cmd),>(cmd)) permite que comandos que esperam nomes de arquivos consumam ou produzam fluxos de forma transparente. tee+>()distribui um fluxo para vários consumidores simultâneos sem reler a origem.- Fan-in combina vários produtores em um único consumidor por meio de um FIFO compartilhado.
- Controle de fluxo e bloqueio são recursos, não erros — mas sempre execute pelo menos um dos lados de um par FIFO em segundo plano para evitar um deadlock.
- Use
pvoumbufferpara adicionar buffers maiores e monitorar a vazão quando as etapas produzirem dados em rajadas.
Essas técnicas são a base da engenharia de dados de alto desempenho em Bash: processe dados em escala de GB com memória constante e máximo paralelismo de CPU/IO.
Perguntas Frequentes
A aula “Pipelines de transmissão e pipes nomeados para maior vazão” é grátis?
Sim — o texto completo de “Pipelines de transmissão e pipes nomeados para maior vazão” é grátis para ler aqui na web. Para praticá-la interativamente (um editor de código integrado e um tutor de IA 24/7) e desbloquear o restante do curso de DevOps Bootcamp, atualize para CoddyKit PRO. O curso de DevOps Bootcamp inclui 4 aulas no total.
O que vou aprender em “Pipelines de transmissão e pipes nomeados para maior vazão”?
Use FIFOs e substituição de processos para transmitir dados entre etapas sem arquivos intermediários. Você pratica DevOps Bootcamp com código prático que executa diretamente no navegador, e um tutor de IA 24/7 responde suas dúvidas enquanto trabalha na aula.
Preciso ter experiência prévia para começar DevOps Bootcamp?
Nenhuma experiência prévia é necessária. DevOps Bootcamp no CoddyKit é estruturado para alunos iniciantes até avançados, então você pode começar aqui ou desde o início e aprender no seu ritmo. Esta é a aula 4 de 4.
Quanto tempo leva a aula “Pipelines de transmissão e pipes nomeados para maior vazão”?
A maioria das aulas CoddyKit leva cerca de 5–10 minutos. Cada uma é compacta e interativa, então você faz progresso constante e retoma exatamente de onde parou entre web e app.
Posso escrever e executar código nesta aula de DevOps Bootcamp?
Sim. Cada aula de DevOps Bootcamp inclui um editor de código integrado, então você escreve e executa código real direto no navegador e recebe feedback de IA instantaneamente — nenhuma configuração local necessária.
Todas as aulas deste curso
- Criação de perfis de desempenho e prevenção de subshells inúteis
- Paralelismo com xargs -P e tarefas em segundo plano
- Orquestração de cargas de trabalho com GNU parallel
- Pipelines de transmissão e pipes nomeados para maior vazão