---
title: "Executar Comandos Externos em Python com o Módulo subprocess"
url: "https://python.dev.br/blog/python-subprocess-comandos-externos/"
markdown_url: "https://python.dev.br/blog/python-subprocess-comandos-externos.MD"
description: "Aprenda a executar comandos externos em Python com o módulo subprocess da biblioteca padrão: subprocess.run, capturar saída, pipes, encoding UTF-8 e cp1252, timeouts, tratamento de erros, segurança contra shell injection e casos de uso brasileiros como converter documentos no LibreOffice, OCR com Tesseract e ffmpeg. Guia prático em português."
date: "2026-07-13"
author: "Equipe Python Brasil"
---

# Executar Comandos Externos em Python com o Módulo subprocess

Aprenda a executar comandos externos em Python com o módulo subprocess da biblioteca padrão: subprocess.run, capturar saída, pipes, encoding UTF-8 e cp1252, timeouts, tratamento de erros, segurança contra shell injection e casos de uso brasileiros como converter documentos no LibreOffice, OCR com Tesseract e ffmpeg. Guia prático em português.


O `subprocess` é o módulo da biblioteca padrão do Python que executa **comandos externos** — qualquer programa instalado no sistema operacional, do `git` ao `libreoffice`, passando por `ffmpeg`, `tesseract`, `pdftk` e ferramentas próprias da sua empresa. Para quem automatiza rotinas no Brasil, ele é a ponte entre o Python e o ecossistema de linha de comando que já existe nos servidores e estações de trabalho: converter um `.docx` em PDF, fazer OCR de uma nota fiscal escaneada, compactar um vídeo, disparar um `git pull` antes de um deploy. Neste tutorial você vai aprender a usar o `subprocess` com exemplos práticos, tratamento de erros robusto e os cuidados de segurança que diferenciam um script que funciona de um script que vira vulnerabilidade.

Se você já [automatiza planilhas](/blog/python-e-excel-openpyxl/), [gera relatórios em PDF](/blog/python-gerar-pdf-relatorios/) ou [edita documentos do Word](/blog/python-e-word-python-docx/), o `subprocess` abre um novo nível de automação: em vez de reimplementar tudo em Python puro, você orquestra as ferramentas certas para cada tarefa. Quem está começando pode revisar o [guia de Python para iniciantes](/blog/python-para-iniciantes-guia-completo/) e a [introdução à automação com Python](/blog/automatizacao-com-python/); aqui o foco é executar processos externos com segurança e controle.

## A regra de ouro: use `subprocess.run`

A API moderna e recomendada é a função `subprocess.run`, disponível desde o Python 3.5. Ela executa o comando, espera terminar e devolve um objeto `CompletedProcess` com tudo o que você precisa:

```python
import subprocess

resultado = subprocess.run(["echo", "Olá, Brasil!"], capture_output=True, text=True)
print(resultado.stdout)        # "Olá, Brasil!\n"
print(resultado.returncode)    # 0 (sucesso)
```

Esqueça o antigo `os.system`: ele só devolve o código de saída numérico, não captura a saída e é menos seguro. O `subprocess.run` é o substituto direto e superior. Outras funções do módulo (`call`, `check_output`, `getoutput`) ainda existem por compatibilidade, mas em código novo `run` cobre todos os casos.

## Argumentos como lista, não como string

O ponto que mais confunde iniciantes: o primeiro argumento de `run` deve ser uma **lista**, em que cada elemento é um argumento separado.

```python
# Correto: cada argumento é um item da lista
subprocess.run(["ls", "-l", "/tmp"])

# Errado (e perigoso): uma string só funciona com shell=True
subprocess.run("ls -l /tmp", shell=True)   # evite!
```

Passar a lista faz o Python invocar o programa **diretamente**, sem acionar um *shell* intermediário. Isso é mais rápido, mais previsível e — o mais importante — **mais seguro**, como veremos adiante.

## Capturando saída e código de retorno

Para ler o que o comando escreveu, use `capture_output=True` junto com `text=True`. Sem `text=True`, a saída vem como `bytes`; com ele, o Python decodifica para `str` usando o encoding padrão do sistema.

```python
resultado = subprocess.run(
    ["git", "log", "--oneline", "-5"],
    capture_output=True,
    text=True,
)

if resultado.returncode == 0:
    print(resultado.stdout)
else:
    print("Erro:", resultado.stderr)
```

O objeto devolvido tem quatro atributos principais:

- `stdout`: a saída padrão (o "resultado normal" do comando).
- `stderr`: a saída de erro (avisos, diagnósticos, mensagens de falha).
- `returncode`: `0` indica sucesso; valores diferentes de zero indicam erro.
- `args`: a lista de argumentos que foi passada.

Para uma referência rápida de como tratar essas falhas, vale conferir o artigo sobre [tratamento de erros em Python](/blog/tratamento-de-erros-python/).

## A pegadinha brasileira: encoding UTF-8 x cp1252

Aqui está um problema clássico para quem trabalha no Brasil. No **Linux** e no **macOS**, a saída dos comandos costuma vir em UTF-8. No **Windows em português**, muitos programas devolvem texto em **cp1252** (Windows-1252), e o encoding padrão do Python no Windows é justamente o cp1252 — o que pode bagunçar acentos quando o texto é UTF-8, ou vice-versa.

A solução é **declarar o encoding explicitamente** em vez de depender do padrão do sistema:

```python
# Tenta UTF-8 primeiro; recua para cp1252 se houver erro de decodificação
try:
    resultado = subprocess.run(
        ["meu_programa.exe"],
        capture_output=True,
        text=True,
        encoding="utf-8",
    )
    saida = resultado.stdout
except UnicodeDecodeError:
    resultado = subprocess.run(
        ["meu_programa.exe"],
        capture_output=True,
    )
    saida = resultado.stdout.decode("cp1252")
```

Uma alternativa robusta para origem desconhecida é capturar como `bytes` (sem `text=True`) e decodificar com `errors="replace"`, que substitui caracteres inválidos em vez de quebrar.

## Validação automática com `check=True`

Em vez de testar `returncode == 0` manualmente, passe `check=True`: se o comando falhar, o Python levanta a exceção `subprocess.CalledProcessError`, que você captura com `try/except`.

```python
import subprocess

try:
    subprocess.run(
        ["git", "push"],
        check=True,
        capture_output=True,
        text=True,
    )
    print("Deploy enviado com sucesso.")
except subprocess.CalledProcessError as erro:
    print(f"Falha (código {erro.returncode}): {erro.stderr}")
```

Isso torna o fluxo de erro explícito e deixa o código mais limpo — especialmente em pipelines que encadeiam várias etapas, como um [deploy de aplicação Python](/blog/deploy-aplicacao-python/).

## Timeout: nunca confie num comando externo

Comandos externos podem travar — um download que nunca termina, um processo que espera entrada que não chega, um servidor que não responde. Sempre que há risco, defina um `timeout` (em segundos):

```python
try:
    resultado = subprocess.run(
        ["curl", "-s", "https://api.exemplo.com/saude"],
        capture_output=True,
        text=True,
        timeout=10,
    )
except subprocess.TimeoutExpired:
    print("O serviço demorou mais de 10 segundos — abortado.")
```

Quando o prazo expira, o Python mata o processo e levanta `TimeoutExpired`. Em scripts de automação que rodam em cron ou em [pipelines orquestrados com Airflow](/blog/airflow-python-orquestracao-pipelines/), o `timeout` é o que separa uma falha limpa de um job pendurado para sempre.

## Entrada de dados via `input`

Para enviar dados para a entrada padrão (*stdin*) de um comando, use o argumento `input`:

```python
resultado = subprocess.run(
    ["python", "-c", "print(input().upper())"],
    input="olá, brasil",
    capture_output=True,
    text=True,
    encoding="utf-8",
)
print(resultado.stdout.strip())   # "OLÁ, BRASIL"
```

Isso substitui o uso frágil de arquivos temporários e é ideal para comandos que esperam texto na entrada.

## Encadeando processos com `Popen`

Quando você precisa conectar a saída de um comando à entrada de outro (o famoso *pipe* `|` do shell) ou controlar o processo sem bloquear, use a classe `subprocess.Popen`:

```python
proc_1 = subprocess.Popen(["ls", "-1"], stdout=subprocess.PIPE)
proc_2 = subprocess.Popen(["wc", "-l"], stdin=proc_1.stdout, stdout=subprocess.PIPE)
proc_1.stdout.close()   # permite que proc_1 receba SIGPIPE se proc_2 terminar
saida, _ = proc_2.communicate()
print(int(saida))       # número de arquivos no diretório
```

O `Popen` é a base de baixo nível sobre a qual o próprio `run` é construído. Use-o quando `run` não for suficiente — por exemplo, para lançar um processo em segundo plano e continuar executando o seu script:

```python
servidor = subprocess.Popen(["python", "-m", "http.server", "8000"])
# o servidor continua rodando; o script segue em frente
print("Servidor iniciado em segundo plano.")
# depois, quando quiser parar:
servidor.terminate()
```

Para concorrência real entre tarefas pesadas em Python, o caminho certo é o módulo [`multiprocessing`](/blog/python-multiprocessing/) ou a programação [assíncrona com `async`/`await`](/blog/python-async-await/); o `subprocess` orquestra processos externos, não substitui o paralelismo interno.

## Segurança: o perigo do `shell=True`

A regra mais importante deste tutorial: **evite `shell=True`**. Quando você passa `shell=True`, o Python monta uma string e a entrega a um *shell* (`/bin/sh` no Linux, `cmd.exe` no Windows), que interpreta caracteres especiais como `;`, `|`, `&&` e `$()`. Se algum desses caracteres vier de um dado externo — um nome de arquivo enviado pelo usuário, um campo de um formulário —, você abre um buraco de **injeção de comandos** (*shell injection*).

```python
# PERIGOSO: se nome_arquivo vier do usuário e contiver
# "; rm -rf ~", o shell executa a remoção
subprocess.run(f"convert {nome_arquivo} saida.png", shell=True)

# SEGURO: a lista isola cada argumento; não há shell para interpretar
subprocess.run(["convert", nome_arquivo, "saida.png"])
```

Na forma de lista, o `nome_arquivo` é sempre tratado como um argumento literal, por mais malicioso que seja o conteúdo. Se você realmente precisa de recursos do shell (curingas `*`, redirecionamento `>`, encadeamento `&&`), construa a string a partir de **dados confiáveis** e nunca de entrada do usuário — e ainda assim prefira resolver a lógica em Python.

## Casos de uso brasileiros na prática

O `subprocess` brilha em automações que combinam o Python com ferramentas de linha de comando consagradas. Alguns cenários comuns no mercado brasileiro:

**Converter documentos no LibreOffice.** Muitas empresas têm o LibreOffice instalado em servidores Linux. Com o modo *headless*, você converte `.docx`, `.xlsx` e `.odt` para PDF sem abrir interface gráfica — uma mão na roda para fechar o ciclo de geração de [documentos](/blog/python-e-word-python-docx/) e [relatórios](/blog/python-gerar-pdf-relatorios/):

```python
subprocess.run(
    ["libreoffice", "--headless", "--convert-to", "pdf", "--outdir", "saida", "contrato.docx"],
    check=True,
    capture_output=True,
    text=True,
    timeout=60,
)
```

**OCR de documentos com Tesseract.** Para ler notas fiscais, comprovantes e boletos escaneados, o Tesseract com o pacote de idioma português (`por`) é o motor de OCR mais acessível:

```python
subprocess.run(
    ["tesseract", "nota_fiscal.png", "saida", "-l", "por"],
    check=True,
    capture_output=True,
    text=True,
    timeout=30,
)
# o texto fica em saida.txt
```

**Compactar e converter mídia com ffmpeg.** Redimensionar vídeos ou extrair áudio de um podcast antes de publicar:

```python
subprocess.run(
    ["ffmpeg", "-y", "-i", "entrada.mp4", "-c:v", "libx265", "-crf", "28", "saida.mp4"],
    check=True,
    timeout=300,
)
```

**Automação de Git.** Disparar um `commit` e `push` ao final de um processamento, conectando-se ao tema de [automação com Git em Python](/blog/python-e-git-automacao/):

```python
subprocess.run(["git", "add", "-A"], check=True)
subprocess.run(["git", "commit", "-m", "Atualização automática diária"], check=True)
subprocess.run(["git", "push"], check=True, capture_output=True, text=True)
```

## Boas práticas que evitam horas de debug

- **Prefira `subprocess.run`** e passe argumentos como **lista**.
- **Nunca use `shell=True`** com dados externos; o risco de injeção é real.
- **Declare o `encoding` explicitamente** (`utf-8` ou `cp1252`) para não depender do padrão do sistema.
- **Sempre defina um `timeout`** quando o comando puder travar.
- Use **`check=True`** para que falhas virem exceções em vez de código silencioso.
- Registre a saída e o código de retorno com o módulo de [logging](/blog/logging-em-python/) em automações de longa duração.
- Trate caminhos de arquivo com [`pathlib`](/blog/manipulacao-de-arquivos-python/) e valide entradas externas antes de passá-las a comandos.

Com o `subprocess` dominado, você transforma o Python em um maestro capaz de reger qualquer ferramenta de linha de comando — do LibreOffice ao Tesseract, do `ffmpeg` ao `git` —, encadeando etapas de automação que respeitam encoding, tratam falhas e rodam com a segurança que a produção exige.
