wlls / devops / shell-script-ou-python-como-decidir-na-automao
devops

Shell script ou Python: como decidir na automação

wander·09 de out.·6 min de leitura

Cenário

Toda equipe de infraestrutura chega nesse cruzamento: precisa automatizar uma tarefa — rotacionar logs, orquestrar um deploy, validar configuração antes de aplicar um manifesto — e alguém pergunta "isso a gente escreve em shell ou em Python?". A resposta correta quase nunca é "sempre X". Depende da complexidade da lógica, de quem vai manter o script depois e de que tipo de falha você não pode tolerar.

Este post não compara benchmarks de performance nem relata incidentes reais — não há fontes de pesquisa disponíveis para este tema, então o que segue é uma análise baseada em critérios técnicos e na documentação oficial de cada ferramenta, não em dados coletados ou experiência relatada em produção. Trate as recomendações como ponto de partida para decisão, não como veredito definitivo.

Premissas

  • Os exemplos de código abaixo não foram executados neste ambiente; revise a sintaxe antes de usar em produção.
  • O post assume Bash como shell de referência (presente por padrão na maioria das distribuições Linux) e Python 3.x como alternativa.
  • Quando a tarefa envolve orquestração multi-host complexa, ferramentas de IaC/configuration management (Ansible, Terraform, Salt) competem com as duas opções abaixo — isso é assunto para outro post, aqui o foco é script único, não uma plataforma de automação completa.

Critérios de decisão

Antes de escolher a linguagem, responda três perguntas:

  1. A tarefa é majoritariamente orquestração de comandos existentes ou lógica de processamento de dados? Encadear tar, rsync, systemctl e curl é o terreno natural do shell. Parsear JSON, validar schemas, fazer retry com backoff exponencial ou manipular estruturas de dados complexas pesa para Python.
  2. Quem vai dar manutenção nisso em seis meses? Um script shell de 200 linhas com muitos if aninhados e parsing de texto frágil é difícil de depurar para quem não escreveu. Python com funções nomeadas, testes e type hints reduz essa fricção.
  3. Qual o custo de um erro silencioso? Shell script tem comportamento de erro traiçoeiro por padrão — um comando que falha no meio de um pipeline pode não interromper a execução se você não configurar isso explicitamente.

Diferenças práticas

Tratamento de erro

Em Bash, erro não interrompe a execução a menos que você configure isso. A prática recomendada é abrir o script com:

#!/usr/bin/env bash
set -euo pipefail
  • set -e: encerra o script se qualquer comando retornar código de saída diferente de zero.
  • set -u: trata variável não definida como erro.
  • set -o pipefail: propaga falha de qualquer estágio de um pipe, não só do último comando.

Essas opções estão documentadas no manual oficial do Bash (gnu.org/software/bash/manual), na seção "The Set Builtin". Mesmo com elas, shell script ainda sofre com falhas de word splitting e expansão de glob se você não citar variáveis corretamente — é um erro comum mesmo em scripts "maduros".

Em Python, exceções são o modelo padrão: um subprocess.run(..., check=True) levanta CalledProcessError se o comando falhar, e você decide explicitamente onde capturar isso:

import subprocess

try:
    subprocess.run(["systemctl", "restart", "nginx"], check=True)
except subprocess.CalledProcessError as exc:
    print(f"Falha ao reiniciar nginx: {exc}")
    raise

O comportamento de subprocess.run e o parâmetro check estão documentados em docs.python.org/3/library/subprocess.html.

Manipulação de dados estruturados

Parsing de JSON ou YAML em shell depende de ferramentas externas (jq, yq) e o resultado ainda é texto — qualquer lógica condicional sobre esse dado volta a ser manipulação de string. Em Python, você carrega o JSON como dicionário nativo:

import json

with open("config.json") as f:
    config = json.load(f)

if config.get("replicas", 1) < 2:
    print("Aviso: replicas insuficientes para alta disponibilidade")

Para configurações de infraestrutura (que cada vez mais são JSON ou YAML), essa diferença pesa bastante a favor de Python conforme a lógica de validação cresce.

Portabilidade e dependências

Shell script roda em qualquer ambiente com Bash (ou até sh POSIX, se você evitar bashismos) sem instalar nada — isso importa em imagens de container minimalistas ou ambientes air-gapped. Python exige o interpretador presente e, se o script usar bibliotecas de terceiros, um ambiente virtual ou gerenciamento de dependências (venv, pip, poetry). Isso é custo operacional real: mais uma coisa para versionar e manter atualizada por questões de segurança.

Legibilidade e manutenção em equipe

Scripts shell curtos (até ~50 linhas, lógica linear) são lidos rapidamente por qualquer pessoa da equipe. Passado esse tamanho, funções, escopo de variável e debugging ficam mais difíceis — não existe um equivalente direto a um debugger interativo robusto como o pdb do Python, e testes automatizados de shell script (com bats, por exemplo) são menos comuns na prática do que testes unitários em Python com pytest.

Quando usar shell script

  • Tarefa é essencialmente orquestrar comandos de sistema já existentes (systemd, pacotes, arquivos).
  • Script roda em ambiente com superfície mínima de dependências (imagem base de container, bootstrap de VM).
  • Lógica é linear, sem necessidade de estruturas de dados complexas.
  • Vai ser chamado dentro de um pipeline CI/CD como um passo isolado e simples.

Quando usar Python

  • Lógica envolve parsing, validação ou transformação de dados estruturados (JSON, YAML, respostas de API).
  • Você precisa de tratamento de erro granular, retries com backoff, ou chamadas a APIs HTTP.
  • O script vai crescer, ganhar testes automatizados e ser mantido por várias pessoas.
  • Você já depende de bibliotecas específicas (SDKs de cloud, boto3, clientes Kubernetes) que tornam Python o caminho natural.

Abordagem híbrida

Na prática, muitos pipelines de automação combinam os dois: um script shell curto cuida do bootstrap (verificar dependências, exportar variáveis de ambiente, invocar o passo principal) e delega a lógica pesada para um script Python. Isso evita reescrever em Python tarefas triviais de orquestração e evita forçar shell a fazer parsing que ele não faz bem.

#!/usr/bin/env bash
set -euo pipefail

if ! command -v python3 &>/dev/null; then
  echo "python3 não encontrado" >&2
  exit 1
fi

python3 ./valida_config.py "$1"

Esse padrão também facilita a migração gradual: você troca partes de um script shell legado por módulos Python conforme a complexidade justifica, sem precisar reescrever tudo de uma vez.

Recomendação condicionada

Se a tarefa é orquestração simples e linear, com dependências mínimas e vida curta, shell script com set -euo pipefail e shellcheck no CI resolve com menos atrito. Se a lógica envolve dados estruturados, tratamento de erro detalhado ou vai crescer além de uma tela de código, comece em Python — o custo inicial de setup (ambiente, dependências) se paga em manutenibilidade.

Essa escolha muda se a equipe já tem um padrão estabelecido: forçar Python numa equipe que só mantém shell (ou vice-versa) troca um problema técnico por um problema de adoção, que costuma ser mais caro. Avalie o script existente antes de decidir a linguagem do próximo.

Como próximo passo prático, pegue um script de automação que você mantém hoje e aplique o checklist das três perguntas da seção de critérios — se a resposta apontar para a linguagem errada, é sinal de que vale planejar a migração antes que o script cresça mais.

Fontes

#shell-script#python#automacao-infraestrutura#tratamento-de-erro#subprocess#bash