Domine o Bash em Python: Execute Scripts e Comandos com `subprocess`

Se você utiliza o sistema operacional Linux, certamente aprecia a versatilidade dos comandos do terminal.

Ao trabalhar com a linguagem Python, é comum buscar formas de automatizar tarefas, o que economiza tempo e esforço. É possível também utilizar scripts Bash para essa finalidade.

No entanto, Python se destaca na criação de scripts, oferecendo maior facilidade de gerenciamento e manutenção em comparação com Bash, especialmente em projetos maiores.

Mas surge a questão: como executar scripts Bash já existentes usando Python?

Existe uma maneira de integrar a execução de comandos e scripts Bash diretamente em scripts Python?

A resposta é sim! Python possui um módulo integrado chamado `subprocess` que permite executar comandos e scripts dentro de seus scripts. Vamos explorar em detalhes como realizar essa integração.

Executando Comandos Bash com Python

O módulo `subprocess` é a chave para a execução de comandos e scripts Bash. Ele oferece diversos métodos e classes para diferentes cenários.

As principais ferramentas dentro do módulo `subprocess` são o método `run()` e a classe `Popen`. Ambos facilitam a execução de comandos Bash em scripts Python. Vamos analisar cada um deles.

`subprocess.run()`

O método `subprocess.run()` recebe uma lista de strings como argumento posicional. O primeiro elemento dessa lista é o comando Bash a ser executado e os demais são os argumentos do comando.

A seguir, um exemplo simples:

import subprocess
subprocess.run(["ls"])

Este script lista todos os arquivos e diretórios presentes no diretório de trabalho atual. Observe que não há argumentos adicionais ao comando `ls`. Podemos adicionar argumentos como `-l`, `-a` ou `-la` para modificar o comportamento do comando.

Veja um exemplo com argumentos:

import subprocess
subprocess.run(["ls", "-la"])

Este comando exibe todos os arquivos, incluindo os ocultos, juntamente com suas permissões. O argumento `-la` combina os efeitos de `-l` (listagem detalhada) e `-a` (incluir arquivos ocultos).

Ao escrever comandos, é possível cometer erros. Para capturá-los e tratá-los, podemos usar o argumento de palavra-chave `stderr`.

Acompanhe o exemplo abaixo:

import subprocess
result = subprocess.run(["cat", "sample.txt"], stderr=subprocess.PIPE, text=True)
print(result.stderr)

Certifique-se de que o arquivo “sample.txt” não exista no diretório de trabalho. O valor `subprocess.PIPE` para `stderr` direciona a saída de erro para um objeto, que pode ser acessado posteriormente. O argumento `text=True` assegura que a saída seja tratada como uma string.

Da mesma forma, podemos capturar a saída padrão de um comando utilizando o argumento de palavra-chave `stdout`:

import subprocess
result = subprocess.run(["echo", "Hello, World!"], stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True)
print(result.stdout)

`subprocess.run()` – Fornecendo Entrada

É possível enviar dados para um comando usando o argumento de palavra-chave `input`. Os dados devem ser fornecidos como uma string. Por isso, é importante definir o argumento `text` como `True`. Por padrão, a entrada é tratada como bytes.

Veja o exemplo:

import subprocess
subprocess.run(["python3", "add.py"], text=True, input="2 3")

Neste caso, o script Python “add.py” recebe os números 2 e 3 como entrada por meio do argumento `input`.

`subprocess.Popen()`

A classe `subprocess.Popen()` oferece recursos mais avançados do que o método `subprocess.run()`. Ela permite um controle mais granular da execução dos comandos, incluindo o status, a captura de saídas e o envio de entradas.

Vamos criar uma instância de `subprocess.Popen()` e explorar seus métodos.

`wait()`

O método `wait()` bloqueia a execução do script Python até que o comando especificado seja concluído. As próximas linhas de código só serão executadas após a finalização do comando anterior. Observe este exemplo:

import subprocess
process = subprocess.Popen(["ls", "-la"])
print("Concluído!")

Ao executar este código, você notará que a mensagem “Concluído!” é exibida antes da execução do comando `ls`. Para que o script espere o comando terminar antes de imprimir a mensagem, use o método `wait()`:

import subprocess
process = subprocess.Popen(["ls", "-la"])
process.wait()
print("Concluído!")

Agora, a mensagem será exibida somente após a execução do comando `ls`.

`communicate()`

O método `communicate()` permite obter a saída padrão, a saída de erro e enviar dados de entrada para o comando. Ele retorna uma tupla contendo a saída padrão e a saída de erro, respectivamente. Veja um exemplo:

import subprocess
process = subprocess.Popen(["echo", "Olá, Mundo!"], stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True)
result = process.communicate()
print(result)

`subprocess.Popen()` – Fornecendo Entrada

Diferentemente do `subprocess.run()`, a classe `Popen` requer o uso do argumento de palavra-chave `stdin` para fornecer entrada ao comando. A instância da classe `Popen` fornecerá o objeto `stdin`. O método `write()` deste objeto é usado para enviar os dados de entrada.

Lembre-se de que, por padrão, a entrada é tratada como bytes. Portanto, defina `text=True` ao criar a instância de `Popen`.

Exemplo:

import subprocess
process = subprocess.Popen(["python3", "add.py"], stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True)
process.stdin.write("2 3")
process.stdin.close()
print(process.stdout.read())

`poll()`

O método `poll()` verifica se a execução do comando foi concluída. Ele retorna `None` se o comando ainda estiver em execução. Acompanhe o exemplo:

import subprocess
process = subprocess.Popen(['ping', '-c 5', 'google.com'], stdout=subprocess.PIPE, text=True)
while True:
    output = process.stdout.readline()
    if output:
    	print(output.strip())
    result = process.poll()
    if result is not None:
        break

Neste código, usamos o comando `ping` com 5 requisições. O loop `while` executa até que o comando seja concluído. O método `poll()` verifica o status da execução, retornando um código diferente de `None` quando o comando é finalizado. Isso encerra o loop.

Executando Scripts Bash

Exploramos duas formas de executar comandos. Agora, vamos aprender como executar scripts Bash dentro de scripts Python.

O módulo `subprocess` possui um método chamado `call()`, utilizado para executar scripts Bash. Este método retorna o código de saída do script Bash. O código de saída padrão para scripts Bash é 0.

Crie um script Bash chamado “practice.sh” com o seguinte conteúdo:

#!/bin/bash
echo "Olá, Mundo!"
exit 1

Agora, crie um script Python para executar o script Bash:

import subprocess
exit_code = subprocess.call('./practice.sh')
print(exit_code)

Ao executar este script Python, você verá a seguinte saída:

Olá, Mundo!
1

Conclusão

Este artigo abordou a execução de comandos e scripts Bash usando Python. Essas ferramentas são úteis para automatizar tarefas de forma mais eficiente.

Bons estudos e boa codificação!