🐚 Como o shell executa comandos
Quando você digita ls *.js | grep test | wc -l e pressiona Enter,
o shell realiza uma sequência precisa de chamadas de sistema: parseia a linha,
cria processos com fork(), conecta-os com pipe()
e substitui cada filho com o programa real via exec(). Tudo em
milissegundos.
① Intuição
O shell é apenas um programa que lê linhas de texto e as transforma em processos.
Ele não executa ls diretamente — ele cria um filho de si mesmo
e depois o filho se transforma no programa pedido.
Pipes são conexões entre processos: a saída de um programa vira a entrada de outro. É como encanamento d'água — cada processo não sabe com quem está conectado, só escreve e lê de file descriptors.
② Visualização — pipeline passo a passo
Avance pelos passos para ver como ls *.js | grep test | wc -l é
tokenizado, parseado, e transformado em 3 processos conectados por pipes.
③ Explicação técnica
fork() + exec() — o par fundamental
fork() duplica o processo atual. exec() substitui a imagem
do processo por um programa diferente. Juntos, permitem que o shell inicialize o filho
com configurações (pipes, variáveis) antes de virar o programa real:
# O que acontece quando você digita um comando import os pid = os.fork() # cria processo filho idêntico ao pai if pid == 0: # FILHO: substituir imagem do processo os.exec('/bin/ls', ['ls', '*.js']) else: # PAI (shell): aguarda filho terminar os.waitpid(pid, 0)
pipe() — conectar processos
Um pipe é um par de file descriptors: o filho escreve em um lado, o pai (ou outro filho)
lê do outro. dup2() rearanja quais fds são stdin/stdout:
import os # Criar um par de file descriptors conectados r_fd, w_fd = os.pipe() # r_fd: leitura, w_fd: escrita pid = os.fork() if pid == 0: # FILHO (ls): stdout → w_fd os.dup2(w_fd, 1) # fd 1 (stdout) agora aponta para w_fd os.close(r_fd) os.exec('/bin/ls', ['ls', '*.js']) else: # PAI: lê de r_fd o que ls escreveu os.close(w_fd) data = os.read(r_fd, 4096)
Redirecionamentos
O shell interpreta >, >> e < antes de criar
os processos — são apenas dup2() sobre arquivos:
# Redirecionamentos que o shell interpreta # > arquivo stdout → arquivo (trunca) ls > lista.txt # >> arquivo stdout → arquivo (append) echo "nova linha" >> lista.txt # 2> stderr → arquivo python script.py 2> erros.log # 2>&1 stderr → mesmo destino que stdout python script.py > tudo.log 2>&1 # < arquivo stdin ← arquivo sort < dados.txt
④ Projeto
Escreva um mini-shell em Python que suporte pipes:
- Leia uma linha com
input() - Divida pelo
|para obter a lista de comandos - Para cada par de comandos adjacente, crie um
os.pipe() - Dê
fork()para cada comando, conecte os pipes comdup2() - O pai chama
waitpid()para todos os filhos - Bônus: adicione suporte a
>(redirect para arquivo)
⑤ Perguntas rápidas
- Se
grepnão encontrar nada e retornar código 1, o que o shell faz? - O que acontece se você escrever
cat | cat | cat? - Por que
cdnão pode ser um programa externo — precisa ser um built-in? - O que o operador
&&faz diferente do|?
⑥ Aplicações no mundo real
- CI/CD pipelines — GitHub Actions, Jenkins: sequências de comandos conectados exatamente como pipes de shell
- ETL de dados —
curl | jq | python | psql: cada etapa transforma e passa adiante - Logs em produção —
journalctl | grep ERROR | tail -f: monitoramento em tempo real via pipe - Linguagens de programação — o módulo
subprocessdo Python echild_processdo Node usam fork+exec+pipe internamente