Exploração

🐚 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.

$ls *.js | grep test | wc -l
Passo 1/8: Você digita o comando
O shell lê a linha digitada como uma string.
1 / 8

③ 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:

  1. Leia uma linha com input()
  2. Divida pelo | para obter a lista de comandos
  3. Para cada par de comandos adjacente, crie um os.pipe()
  4. fork() para cada comando, conecte os pipes com dup2()
  5. O pai chama waitpid() para todos os filhos
  6. Bônus: adicione suporte a > (redirect para arquivo)

⑤ Perguntas rápidas

⑥ Aplicações no mundo real