Integrando shell com outras linguagens
LidoNesta aula, você aprenderá a integrar shell scripts com outras linguagens, principalmente Python, cobrindo desde a chamada de scripts até a passagem de dados, além de entender quando faz sentido migrar de linguagem. Inclui exemplos práticos, boas práticas e exercícios com respostas.
O shell é uma ferramenta poderosa para automação e orquestração de tarefas, mas nem sempre é a melhor escolha para processamento complexo de dados, manipulação de arquivos binários, ou implementação de algoritmos avançados. Integrar o shell com outras linguagens, como Python, permite combinar a simplicidade de scripts de shell com a expressividade e bibliotecas de linguagens de propósito geral. Nesta aula, vamos explorar como fazer essa integração de forma eficiente, passando dados entre o shell e outras linguagens, e discutir quando vale a pena migrar parte do código para uma linguagem mais robusta.
Vamos começar com a forma mais simples: chamar scripts Python a partir do shell. Em seguida, veremos como passar argumentos, usar saída de programas, e até mesmo comunicação bidirecional. Ao final, você terá um repertório de técnicas para decidir quando usar shell puro e quando integrar outra linguagem.
Chamando Python/scripts
A forma mais comum de integrar shell com Python é simplesmente invocar o interpretador Python dentro de um script de shell. Isso pode ser feito de várias maneiras, dependendo da necessidade: chamar um script Python existente, executar código Python inline, ou até mesmo usar shebang para tornar o script Python executável diretamente.
Para chamar um script Python de dentro de um script shell, basta usar o comando python3 (ou python dependendo do sistema) seguido do caminho do script. Por exemplo, se você tem um arquivo process.py que recebe argumentos, você pode chamá-lo assim:
#!/bin/bash
# script.sh
python3 process.py "arg1" "arg2"
Se você quiser executar um trecho de código Python diretamente sem criar um arquivo separado, pode usar a opção -c do interpretador:
python3 -c "print('Olá do Python')"
Outra abordagem é usar python3 - para ler o código da entrada padrão, o que permite passar código via pipe:
echo "print('Hello from pipe')" | python3 -
Além disso, é possível tornar um script Python executável diretamente, adicionando um shebang no início do arquivo Python e marcando-o como executável. Assim, você pode chamar o script diretamente pelo nome, como se fosse um comando do shell:
#!/usr/bin/env python3
# process.py
import sys
print('Argumentos:', sys.argv[1:])
chmod +x process.py
./process.py "arg1" "arg2"
Essa abordagem é útil para scripts que são essencialmente Python, mas que precisam ser integrados em pipelines de shell. Lembre-se de que o shebang deve apontar para o interpretador correto (no Linux, geralmente /usr/bin/env python3 é mais portátil).
Passando dados
Passar dados entre o shell e outras linguagens pode ser feito de várias formas: argumentos de linha de comando, entrada padrão, variáveis de ambiente, arquivos temporários, e até mesmo pipes bidirecionais. A escolha depende da quantidade de dados e da natureza da comunicação.
Argumentos de linha de comando são ideais para pequenas quantidades de dados, como parâmetros de configuração. No shell, você pode passar variáveis como argumentos:
nome="Maria"
idade=30
python3 saudacao.py "$nome" "$idade"
No Python, esses argumentos são acessíveis via sys.argv.
Entrada padrão é útil para dados maiores, como conteúdo de arquivos ou saída de outros comandos. Você pode usar um pipe para enviar dados para o script Python:
cat dados.txt | python3 processa_dados.py
Ou, no script Python, ler de sys.stdin:
import sys
dados = sys.stdin.read()
print('Recebido:', len(dados), 'caracteres')
Variáveis de ambiente são outra forma de passar dados, especialmente para configurações. No shell, você define a variável e a exporta:
export MODO="producao"
python3 script.py
No Python, você acessa via os.environ.
Arquivos temporários são adequados para dados estruturados (como JSON ou CSV) que precisam ser persistidos entre execuções. O shell pode criar um arquivo com a saída de um comando e depois passar o caminho para o Python:
comando_que_gera_dados > /tmp/dados.json
python3 processa.py /tmp/dados.json
Finalmente, pipes bidirecionais permitem comunicação contínua entre o shell e o Python, mas são mais complexos. Você pode usar coproc no Bash ou ferramentas como expect. Para a maioria dos casos, os métodos anteriores são suficientes.
Um exemplo prático de passagem de dados via pipe e argumentos:
#!/bin/bash
# Soma números passados por uma lista
numeros="1 2 3 4 5"
echo "$numeros" | python3 -c "
import sys
nums = [int(x) for x in sys.stdin.read().split()]
print('Soma:', sum(nums))
"
Quando migrar de linguagem
Saber quando migrar de shell para uma linguagem como Python é crucial para manter a produtividade e a manutenibilidade do código. O shell é excelente para orquestrar comandos, manipular arquivos de texto, e automação de tarefas simples. No entanto, ele se torna frágil quando a lógica fica complexa, quando há necessidade de estruturas de dados avançadas, ou quando o desempenho é crítico.
Aqui estão alguns sinais de que você deve considerar migrar parte do código para Python ou outra linguagem:
- Lógica condicional complexa: se você está aninhando muitos
ifecaseno shell, Python oferece estruturas mais claras. - Manipulação de dados estruturados: JSON, CSV, XML, etc. Python tem bibliotecas robustas (como
json,csv,xml.etree) que simplificam o processamento. - Expressões regulares avançadas: embora o shell tenha
grepesed, Python fornece o módulorecom mais controle e legibilidade. - Operações matemáticas ou científicas: Python com NumPy/SciPy é muito mais adequado.
- Desenvolvimento de aplicações maiores: se o script está crescendo além de algumas centenas de linhas, considere uma linguagem mais estruturada.
- Testabilidade e manutenção: Python permite escrever testes unitários facilmente, enquanto testar shell é mais difícil.
No entanto, não é necessário migrar tudo de uma vez. Uma estratégia comum é manter o shell para orquestração e chamar Python para tarefas específicas que exigem mais poder. Por exemplo, um script de backup pode usar shell para montar diretórios e chamar Python para compactar e criptografar.
Um exemplo de quando migrar: se você precisa processar um arquivo CSV grande com validações e transformações, o shell pode se tornar lento e difícil de manter. Em Python, você pode usar a biblioteca csv e pandas para fazer isso de forma eficiente.
Exemplos
Vamos consolidar o que aprendemos com alguns exemplos práticos de integração shell+Python.
Exemplo 1: Chamar um script Python para processar dados de um arquivo
Suponha que você tenha um arquivo vendas.csv com colunas produto,quantidade,preco. Você quer calcular o total de vendas. Em vez de fazer isso com awk (que seria possível, mas menos legível), você pode usar Python:
#!/bin/bash
# total_vendas.sh
python3 - <
Note o uso de <<EOF (here-document) para passar o código Python diretamente no script shell. Isso é útil para scripts pequenos.
Exemplo 2: Usar a saída de um comando Python em uma variável do shell
#!/bin/bash
# capturar_saida.sh
resultado=$(python3 -c "print('Olá mundo')")
echo "O Python disse: $resultado"
Isso usa substituição de comando para capturar a saída padrão do Python em uma variável.
Exemplo 3: Passar argumentos e receber saída
#!/bin/bash
# soma.py
import sys
nums = [int(x) for x in sys.argv[1:]]
print(sum(nums))
#!/bin/bash
# chama_soma.sh
resultado=$(python3 soma.py 10 20 30)
echo "A soma é: $resultado"
Esse padrão é útil para funções matemáticas ou processamento que produzem um resultado único.
Exemplo 4: Comunicação bidirecional (avançado)
Para comunicação mais complexa, você pode usar coproc no Bash, que permite manter um processo em execução e interagir com ele. Exemplo:
#!/bin/bash
coproc python3 -i
# Envia um comando para o Python
echo "print(2+2)" >&"${COPROC[1]}"
# Lê a saída
read -r resultado <&"${COPROC[0]}"
echo "Resultado: $resultado"
Nesse exemplo, python3 -i inicia um interpretador interativo, e usamos o descritor de arquivo para enviar comandos e ler a saída. Isso é avançado e raramente necessário, mas mostra o potencial.
Boas práticas e observações finais
Ao integrar shell com outras linguagens, siga estas boas práticas para evitar problemas:
- Use
set -eno shell para que o script falhe se algum comando falhar, evitando que erros passem despercebidos. - Capture e trate erros do script Python verificando o código de saída (
$?) e a saída de erro (stderr). - Prefira passar dados via entrada padrão para grandes volumes, em vez de argumentos, para evitar limites de tamanho.
- Use here-documents com cuidado: se o código Python contém caracteres especiais como
$, pode ser necessário escapar ou usar<<'EOF'para evitar expansão pelo shell. - Considere usar
python3 -cpara trechos curtos, mas para scripts mais longos, crie um arquivo separado para melhor legibilidade e manutenção. - Documente as interfaces entre shell e Python (argumentos, formatos de dados) para facilitar a colaboração.
Em resumo, integrar shell com outras linguagens é uma habilidade valiosa para qualquer desenvolvedor de sistemas. O shell continua sendo a ferramenta de orquestração por excelência, mas ao combinar com Python, você ganha expressividade e robustez. Use as técnicas apresentadas para criar scripts poderosos e manuteníveis.
Referências
- Python Tutorial: The Interpreter
- Python sys module documentation
- Bash Reference Manual
- Advanced Bash-Scripting Guide
- Python subprocess module
- sh library for calling shell from Python
- Bash Coprocesses
Exercícios
-
Exercício 1: Escreva um script shell que chame um script Python para calcular o fatorial de um número passado como argumento. O script Python deve estar em um arquivo separado
fatorial.pye deve receber o número via argumento de linha de comando.✓ Resposta:fatorial.py:
import sys def fatorial(n): if n < 0: return None if n == 0: return 1 return n * fatorial(n-1) if __name__ == "__main__": if len(sys.argv) != 2: print("Uso: python3 fatorial.py N", file=sys.stderr) sys.exit(1) n = int(sys.argv[1]) print(fatorial(n))script.sh:
#!/bin/bash if [ $# -ne 1 ]; then echo "Uso: $0 N" exit 1 fi resultado=$(python3 fatorial.py "$1") if [ $? -eq 0 ]; then echo "Fatorial de $1 é $resultado" else echo "Erro ao calcular fatorial" fi -
Exercício 2: Crie um script shell que use um pipe para enviar o conteúdo de um arquivo de texto para um script Python que conta o número de palavras. O script Python deve ler da entrada padrão.
✓ Resposta:#!/bin/bash # conta_palavras.sh if [ $# -lt 1 ]; then echo "Uso: $0 arquivo" exit 1 fi cat "$1" | python3 -c " import sys texto = sys.stdin.read() palavras = texto.split() print('Número de palavras:', len(palavras)) " -
Exercício 3: Escreva um script shell que defina uma variável de ambiente e a passe para um script Python, que a imprime. Use
exportno shell eos.environno Python.✓ Resposta:#!/bin/bash # script_env.sh export MINHA_VAR="Olá do shell" python3 -c " import os print('Variável recebida:', os.environ.get('MINHA_VAR')) " -
Exercício 4: Crie um script shell que capture a saída de um comando Python (por exemplo, a data atual formatada) em uma variável e a use em uma mensagem. Use substituição de comando.
✓ Resposta:#!/bin/bash # data_python.sh data=$(python3 -c "from datetime import datetime; print(datetime.now().strftime('%d/%m/%Y %H:%M'))") echo "Data e hora atual: $data" -
Exercício 5: Escreva um script shell que use um here-document para passar um código Python que lê um arquivo CSV e imprime a média de uma coluna. O script shell deve aceitar o nome do arquivo como argumento.
✓ Resposta:#!/bin/bash # media_csv.sh if [ $# -lt 2 ]; then echo "Uso: $0 arquivo.csv coluna" exit 1 fi python3 - "$1" "$2" <<EOF import csv import sys arquivo = sys.argv[1] coluna = sys.argv[2] total = 0 count = 0 with open(arquivo, newline='') as f: reader = csv.DictReader(f) for row in reader: if coluna in row: try: total += float(row[coluna]) count += 1 except ValueError: pass if count > 0: print(f'Média da coluna {coluna}: {total/count:.2f}') else: print('Nenhum dado válido encontrado.') EOF