subprocess inicia programas externos e conecta entrada, saída e código de retorno. A interface atravessa a fronteira entre Python e o sistema operacional, o que exige argumentos, tempo e ambiente controlados.
Usar run() com argumentos separados
import subprocess
resultado = subprocess.run(
["git", "rev-parse", "--short", "HEAD"],
check=True,
capture_output=True,
text=True,
timeout=10,
)
commit = resultado.stdout.strip()
Uma lista envia cada argumento separadamente e evita interpretação automática pelo shell. check=True transforma falha em exceção, timeout limita espera e text=True decodifica a saída.
Evitar injeção e ambiente imprevisível
Não concatene entrada do usuário em uma string executada com shell=True. Metacaracteres podem criar comandos adicionais. Valide valores por allowlist e passe-os como itens separados. Há particularidades para arquivos .bat e .cmd no Windows; revise a seção de segurança da documentação.
Defina cwd explicitamente quando caminhos relativos forem necessários. Passe um env controlado sem apagar variáveis essenciais e nunca inclua segredos em logs. Para saída grande, use Popen.communicate() ou arquivos, evitando leituras manuais que podem bloquear pipes.
Bandit pode sinalizar padrões perigosos, como mostra o guia de análise de segurança com Bandit. Ainda assim, a revisão precisa entender de onde vêm os argumentos.
A documentação oficial de subprocess, consultada em 22 de julho de 2026, recomenda run() nos casos que ele atende e detalha timeout, pipes e segurança. Teste também falha, timeout e executável ausente.
Separar executável e argumentos
Com shell=False, o Python inicia o programa diretamente. Cada item da lista vira um argumento, então espaços no nome de arquivo não o dividem e metacaracteres não ganham significado de shell. Não use command.split(): regras de aspas variam por plataforma. Construa a lista com partes conhecidas.
Separação não elimina a validação. Um valor iniciado por - pode virar opção do programa, e um caminho irrestrito pode expor arquivo indevido. Mapeie escolhas públicas para argumentos internos:
FORMATOS = {
"curto": ["--format", "short"],
"json": ["--format", "json"],
}
def gerar_relatorio(formato: str) -> str:
if formato not in FORMATOS:
raise ValueError("formato não suportado")
resultado = subprocess.run(
["/opt/acme/bin/report", *FORMATOS[formato]],
check=True,
capture_output=True,
text=True,
timeout=15,
)
return resultado.stdout
Um caminho absoluto não depende de PATH. Quando a descoberta for intencional, use shutil.which() e valide o resultado.
Tratar as falhas esperadas
check=True gera CalledProcessError para status não zero. Executável ausente gera FileNotFoundError; limite vencido gera TimeoutExpired:
try:
concluido = subprocess.run(
["git", "status", "--porcelain=v1"],
check=True,
capture_output=True,
text=True,
timeout=5,
)
except subprocess.TimeoutExpired as exc:
raise RuntimeError("git excedeu o tempo") from exc
except subprocess.CalledProcessError as exc:
raise RuntimeError(f"git terminou com status {exc.returncode}") from exc
except FileNotFoundError as exc:
raise RuntimeError("git não está instalado") from exc
Não devolva stderr bruto ao navegador: ele pode conter caminhos, ambiente ou tokens. Registre diagnóstico sanitizado com acesso controlado e entregue mensagem estável. Alguns programas usam status não zero para “diferenças encontradas”; consulte o contrato antes de usar check=True.
Limitar tempo, saída e entrada
timeout limita a espera, embora a criação do processo possa não ser interrompível em toda plataforma. Ao expirar, run() mata o filho, aguarda e gera TimeoutExpired. Teste com a ferramenta real, sobretudo se ela iniciar descendentes.
capture_output=True guarda os dois fluxos na memória. É adequado para respostas pequenas, não para backup ou vídeo. Redirecione saída grande para arquivo já aberto:
from pathlib import Path
destino = Path("/var/lib/acme/relatorio.json")
with destino.open("wb") as saida:
subprocess.run(
["/opt/acme/bin/report", "--json"],
stdout=saida,
stderr=subprocess.PIPE,
check=True,
timeout=60,
)
Com Popen, ler stdout e ignorar um stderr cheio pode bloquear. Prefira communicate() salvo quando houver streaming cuidadosamente testado. Não aceite entrada, duração ou captura ilimitadas em uma requisição web.
Passe conteúdo por input=, não pela linha de comando, que costuma aparecer em listagens:
resultado = subprocess.run(
["/usr/bin/tool", "--read-stdin"],
input=conteudo,
text=True,
capture_output=True,
check=True,
timeout=10,
)
Para segredos, prefira mecanismo protegido oferecido pelo programa. Variáveis de ambiente são menos visíveis que argumentos, mas não formam uma fronteira universal.
Controlar diretório e ambiente
Caminhos relativos dependem de cwd; comportamento herdado depende de env. Use um diretório confiável e resolvido. Nunca permita que entrada externa escolha livremente o diretório de trabalho.
Passar env substitui todo o ambiente do filho. Comece com os.environ.copy() se ele precisar das variáveis normais e sobrescreva apenas as desejadas. Para isolamento maior, crie ambiente mínimo. Remova variáveis perigosas de loader ou linguagem ao iniciar ferramentas privilegiadas e nunca registre o mapa completo.
Use encoding="utf-8" e errors="strict" se a ferramenta garantir UTF-8. Apenas text=True usa o encoding da localidade, que pode variar. Para bytes arbitrários, permaneça no modo binário.
Reservar o shell para sintaxe de shell
Pipelines, redirecionamento, curingas e comandos internos podem exigir shell. Prefira redirecionar com stdin e stdout, e formar pipelines com objetos Popen. Se o shell for indispensável, mantenha a string constante e impeça dados externos de entrar nela. shlex.quote() é voltado a shells POSIX e não sanitiza de forma portátil o Windows.
Arquivos batch no Windows podem envolver parsing do sistema mesmo com shell=False. Siga a nota de segurança oficial e teste espaços e metacaracteres; não presuma que o comportamento do Linux se repete.
Processos longos e testes
Use Popen quando precisar transmitir dados, enviar sinais ou administrar o filho. Utilize-o como gerenciador de contexto, chame communicate() para recolher o processo e defina o desligamento de descendentes. Não inicie trabalho destacado em requisição web sem uma fila supervisionada que cuide de ciclo de vida, repetição, logs e limites.
Teste sucesso, status documentados, executável ausente, timeout, encoding inválido, stderr grande, nomes com espaços e argumentos com aparência hostil. Verifique que cada valor permanece um argumento. Mocks cobrem ramos da aplicação, mas um teste de integração deve executar a ferramenta real.
O padrão seguro combina executável fixo, lista explícita e validada, shell=False, tempo e saída limitados, ambiente deliberado e status tratado. Isso torna o processo previsível sem confundir separação de argumentos com validação da semântica do programa.
Revisão operacional
Antes da implantação, confira permissões dos arquivos e a conta do sistema usada pelo filho. subprocess não cria uma sandbox: o programa herda as capacidades efetivas do pai, salvo restrições externas. Execute a aplicação com privilégio mínimo, mantenha diretórios graváveis restritos e aplique controles do sistema para CPU, memória e quantidade de processos ao executar ferramentas custosas.
Defina também o contrato de observabilidade. Registre nome lógico da operação, duração, status e ocorrência de timeout, mas não a linha de comando inteira quando houver dados sensíveis. Use um identificador de correlação para relacionar o processo à requisição. Métricas de duração e falha ajudam a detectar uma atualização do executável que ficou lenta sem expor seu conteúdo.
Se vários processos iguais podem rodar ao mesmo tempo, considere limites de concorrência. O timeout de cada execução não impede cem filhos de esgotarem o servidor. Uma fila ou semáforo oferece pressão de retorno e permite recusar excesso de trabalho de forma previsível.
Ao atualizar a ferramenta externa, valide sua versão e opções suportadas. Um argumento antes aceito pode mudar de semântica, e mensagens ou encoding podem variar. Fixar uma versão compatível e executar testes de integração reduz surpresas.