0Pricing
Linux Command Line & Bash Scripting Mastery · Aula

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 Linux Command Line & Bash Scripting Mastery 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 Linux Command Line & Bash Scripting Mastery, e seu progresso é sincronizado entre a web e o app CoddyKit. O curso de Linux Command Line & Bash Scripting Mastery 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 -20

Criando 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 rm quando 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_pipe

Substituiçã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_pipe

Usando 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 mbuffer ou pv para adicionar um buffer maior na memória entre as etapas.
  • Use pv -q -B 128m para 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 -c

Exemplo 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/null

Comparando 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 pv ou mbuffer para 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 Linux Command Line & Bash Scripting Mastery, atualize para CoddyKit PRO. O curso de Linux Command Line & Bash Scripting Mastery 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 Linux Command Line & Bash Scripting Mastery 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 Linux Command Line & Bash Scripting Mastery?

Nenhuma experiência prévia é necessária. Linux Command Line & Bash Scripting Mastery 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 Linux Command Line & Bash Scripting Mastery?

Sim. Cada aula de Linux Command Line & Bash Scripting Mastery 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

  1. Criação de perfis de desempenho e prevenção de subshells inúteis
  2. Paralelismo com xargs -P e tarefas em segundo plano
  3. Orquestração de cargas de trabalho com GNU parallel
  4. Pipelines de transmissão e pipes nomeados para maior vazão
← Voltar para Linux Command Line & Bash Scripting Mastery