O que é um subprocesso em Python? [5 Usage Examples]

Os subprocessos permitem que você interaja em um nível totalmente novo com o Sistema Operativo.

Nosso computador executa subprocessos o tempo todo. Na verdade, apenas lendo este artigo, você está executando vários processos, como um gerenciador de rede ou o próprio navegador da Internet.

O legal disso é que qualquer ação que fazemos em nosso computador envolve invocar um subprocesso. Isso permanece verdadeiro mesmo se estivermos escrevendo um simples script “hello world” em python.

O conceito de subprocesso pode parecer obscuro, mesmo que você esteja aprendendo programação há algum tempo. Este artigo examinará profundamente o conceito principal do subprocesso e como usar o Python biblioteca padrão de subprocesso.

Ao final deste tutorial, você:

  • Entenda o conceito de subprocesso
  • Ter aprendido o básico da biblioteca de subprocessos Python
  • Praticou suas habilidades em Python com exemplos úteis

Vamos entrar nisso

O conceito de subprocesso

Em termos gerais, um subprocesso é um processo de computador criado por outro processo.

Podemos pensar em um subprocesso como uma árvore, na qual cada processo pai tem processos filhos sendo executados atrás dele. Sei que isso pode ser bastante confuso, mas vamos ver com um gráfico simples.

Existem várias maneiras de visualizar o processo em execução em nosso computador. Por exemplo, no UNIX (Linux & MAC) temos htop, que é um visualizador de processo interativo.

O modo de árvore é a ferramenta mais útil para dar uma olhada nos subprocessos em execução. Podemos ativá-lo com F5.

Se olharmos atentamente para a seção de comandos, podemos notar a estrutura dos processos em execução em nosso computador.

Tudo começa com /sbin/init que é o comando que inicia cada processo em nosso computador. A partir desse ponto, podemos ver o início de outros processos como o xfce4-screenshoter e o xfce4-terminal (que conduz a ainda mais subprocessos)

Dando uma olhada no Windows, temos o mítico gerenciador de tarefas o que resulta útil ao matar esses programas travados em nossa máquina.

Agora temos um conceito cristalino. Vamos ver como podemos implementar subprocessos em Python.

Subprocessos em Python

Um subprocesso em Python é uma tarefa que um script python delega ao sistema operacional (SO).

A biblioteca de subprocessos nos permite executar e gerenciar subprocessos diretamente do Python. Isso envolve trabalhar com o stdin de entrada padrão, stdout de saída padrão e códigos de retorno.

Não precisamos instalá-lo com PIP, pois faz parte do Python biblioteca padrão.

Portanto, podemos começar a usar subprocessos em python apenas importando o módulo.

import subprocess

# Using the module ....

Observação: para acompanhar este artigo, você deve ter o Python 3.5 +

Para verificar a versão do python que você possui atualmente, basta executar.

❯ python --version
Python 3.9.5 # My result

Caso a versão do Python obtida seja 2.x, você pode usar o seguinte comando

python3 --version

Continuando com o tópico, a ideia principal por trás da biblioteca de subprocessos é poder interagir com o SO executando quaisquer comandos que quisermos, diretamente do interpretador Python.

Isso significa que podemos fazer o que quisermos, desde que nosso sistema operacional nos permita (e desde que você não remova seu sistema de arquivos raiz 😅).

  5 melhores VPNs para Kindle Fire para streaming fácil

Vamos ver como usá-lo criando um script simples que lista os arquivos do diretório atual.

Primeiro aplicativo de subprocesso

Primeiro, vamos criar um arquivo list_dir.py. Este será o arquivo onde vamos experimentar os arquivos de listagem.

touch list_dir.py

Agora vamos abrir esse arquivo e usar o seguinte código.

import subprocess 

subprocess.run('ls')

Primeiro, estamos importando o módulo de subprocesso e, em seguida, usando a função run que executa, o comando que passamos como argumento.

Esta função foi introduzida no Python 3.5, como um atalho amigável para subprocess.Popen. A função subprocess.run nos permite executar um comando e esperar que ele termine, ao contrário do Popen, onde temos a opção de chamar a comunicação posteriormente.

Falando sobre a saída do código, ls é um comando UNIX que lista os arquivos do diretório em que você está. Portanto, se você executar este comando, obterá uma lista dos arquivos presentes no diretório atual.

❯ python list_dir.py
example.py  LICENSE  list_dir.py  README.md

Observação: leve em consideração que, se você estiver no Windows, precisará usar comandos diferentes. Por exemplo, em vez de usar “ls” você pode usar “dir”

Isso pode parecer muito simples, e você está certo. Você quer ter uma abordagem completa de todo o poder que a casca lhe traz. Então vamos aprender como passar argumentos para o shell com subprocess.

Por exemplo, para listar também os arquivos ocultos (aqueles que começam com um ponto) e também listar todos os metadados dos arquivos, escrevemos o seguinte código.

import subprocess

# subprocess.run('ls')  # Simple command

subprocess.run('ls -la', shell=True)

Estamos executando este comando como uma string e usando o argumento shell. Isso significa que estamos invocando um shell no início da execução de nosso subprocesso, e o argumento do comando é interpretado diretamente pelo shell.

No entanto, o uso shell=True tem muitas desvantagens, e o pior são os possíveis vazamentos de segurança. Você pode ler sobre eles no documentação oficial.

A melhor maneira de passar comandos para a função run é usar uma lista onde lst[0] é o comando para chamar (ls neste caso) e lst[n] são os argumentos desse comando.

Se fizermos isso, nosso código ficará assim.

import subprocess

# subprocess.run('ls')  # Simple command

# subprocess.run('ls -la', shell=True) # Dangerous command

subprocess.run(['ls', '-la'])

Se quisermos armazenar a saída padrão de um subprocesso em uma variável, podemos fazê-lo definindo o argumento capture_output como true.

list_of_files = subprocess.run(['ls', '-la'], capture_output=True)

print(list_of_files.stdout)

❯ python list_dir.py 
b'total 36ndrwxr-xr-x 3 daniel daniel 4096 may 20 21:08 .ndrwx------ 30 daniel daniel 4096 may 20 18:03 ..n-rw-r--r-- 1 daniel daniel 55 may 20 20:18 example.pyndrwxr-xr-x 8 daniel daniel 4096 may 20 17:31 .gitn-rw-r--r-- 1 daniel daniel 2160 may 17 22:23 .gitignoren-rw-r--r-- 1 daniel daniel 271 may 20 19:53 internet_checker.pyn-rw-r--r-- 1 daniel daniel 1076 may 17 22:23 LICENSEn-rw-r--r-- 1 daniel daniel 216 may 20 22:12 list_dir.pyn-rw-r--r-- 1 daniel daniel 22 may 17 22:23 README.mdn'

Para acessar a saída de um processo, usamos o atributo de instância stdout.

Nesse caso, queremos armazenar a saída como uma string, em vez de bytes, e podemos fazer isso definindo o argumento text como true.

list_of_files = subprocess.run(['ls', '-la'], capture_output=True, text=True)

print(list_of_files.stdout)

❯ python list_dir.py
total 36
drwxr-xr-x  3 daniel daniel 4096 may 20 21:08 .
drwx------ 30 daniel daniel 4096 may 20 18:03 ..
-rw-r--r--  1 daniel daniel   55 may 20 20:18 example.py
drwxr-xr-x  8 daniel daniel 4096 may 20 17:31 .git
-rw-r--r--  1 daniel daniel 2160 may 17 22:23 .gitignore
-rw-r--r--  1 daniel daniel  271 may 20 19:53 internet_checker.py
-rw-r--r--  1 daniel daniel 1076 may 17 22:23 LICENSE
-rw-r--r--  1 daniel daniel  227 may 20 22:14 list_dir.py
-rw-r--r--  1 daniel daniel   22 may 17 22:23 README.md

Perfeito, agora que conhecemos o básico da biblioteca de subprocessos, é hora de passar para alguns exemplos de uso.

  Como converter um arquivo CSV para VCF para transferir contatos

Exemplos de uso de subprocesso em Python

Nesta seção, revisaremos alguns usos práticos da biblioteca de subprocessos. Você pode conferir todos eles neste Repositório Github.

verificador de programa

Um dos principais usos dessa biblioteca é a capacidade de fazer operações simples do sistema operacional.

Por exemplo, um script simples que verifica se um programa está instalado. No Linux, podemos fazer isso com o comando which.

'''Program checker with subprocess'''

import subprocess

program = 'git'

process = subprocess. run(['which', program], capture_output=True, text=True)

if process.returncode == 0: 
    print(f'The program "{program}" is installed')

    print(f'The location of the binary is: {process.stdout}')
else:
    print(f'Sorry the {program} is not installed')

    print(process.stderr)

Nota: No UNIX, quando um comando é bem-sucedido, seu código de status é 0. Caso contrário, algo deu errado durante a execução

Como não estamos usando o argumento shell=True, podemos aceitar a entrada do usuário com segurança. Além disso, podemos verificar se a entrada é um programa válido com um padrão regex.

import subprocess

import re

programs = input('Separe the programs with a space: ').split()

secure_pattern = 'qua'

for program in programs:

    if not re.match(secure_pattern, program):
        print("Sorry we can't check that program")

        continue

    process = subprocess. run(
        ['which', program], capture_output=True, text=True)

    if process.returncode == 0:
        print(f'The program "{program}" is installed')

        print(f'The location of the binary is: {process.stdout}')
    else:
        print(f'Sorry the {program} is not installed')

        print(process.stderr)

    print('n')

Nesse caso, estamos obtendo os programas do usuário e usando uma expressão regex que certifica que a string do programa inclui apenas letras e dígitos. Verificamos a existência de cada programa com um loop for a.

Grep simples em Python

Seu amigo Tom tem uma lista de padrões em um arquivo de texto e outro arquivo grande no qual deseja obter o número de correspondências para cada padrão. Ele passava horas executando o comando grep para cada padrão.

Felizmente, você sabe como resolver esse problema com Python e o ajudará a realizar essa tarefa em poucos segundos.

import subprocess

patterns_file="patterns.txt"
readfile="romeo-full.txt"

with open(patterns_file, 'r') as f:
    for pattern in f:
        pattern = pattern.strip()

        process = subprocess.run(
            ['grep', '-c', f'{pattern}', readfile], capture_output=True, text=True)

        if int(process.stdout) == 0:
            print(
                f'The pattern "{pattern}" did not match any line of {readfile}')

            continue

        print(f'The pattern "{pattern}" matched {process.stdout.strip()} times')

Dando uma olhada neste arquivo, definimos duas variáveis ​​que são os nomes dos arquivos com os quais queremos trabalhar. Em seguida, abrimos o arquivo que contém todos os padrões e iteramos sobre eles. Em seguida, chamamos um subprocesso que executa um comando grep com o sinalizador “-c” (significa contagem) e determinamos a saída da correspondência com uma condicional.

Se você executar este arquivo (lembre-se que você pode baixar os arquivos de texto do repositório Github)

Configure um virtualenv com subprocess

Uma das coisas mais legais que você pode fazer com o Python é a automação de processos. Esse tipo de script pode economizar horas por semana.

Por exemplo, vamos criar um script de configuração que cria um ambiente virtual para nós e tenta encontrar um arquivo requirements.txt no diretório atual para instalar todas as dependências.

import subprocess

from pathlib import Path


VENV_NAME = '.venv'
REQUIREMENTS = 'requirements.txt'

process1 = subprocess.run(['which', 'python3'], capture_output=True, text=True)

if process1.returncode != 0:
    raise OSError('Sorry python3 is not installed')

python_bin = process1.stdout.strip()

print(f'Python found in: {python_bin}')

process2 = subprocess.run('echo "$SHELL"', shell=True, capture_output=True, text=True)

shell_bin = process2.stdout.split('/')[-1]

create_venv = subprocess.run([python_bin, '-m', 'venv', VENV_NAME], check=True)

if create_venv.returncode == 0:
    print(f'Your venv {VENV_NAME} has been created')

pip_bin = f'{VENV_NAME}/bin/pip3'

if Path(REQUIREMENTS).exists():
    print(f'Requirements file "{REQUIREMENTS}" found')
    print('Installing requirements')
    subprocess.run([pip_bin, 'install', '-r', REQUIREMENTS])

    print('Process completed! Now activate your environment with "source .venv/bin/activate"')

else:
    print("No requirements specified ...")

  Xbox Series X x Xbox Series S: qual você deve comprar?

Nesse caso, estamos usando vários processos e analisando os dados de que precisamos em nosso script python. Também estamos usando o pathlib biblioteca que nos permite descobrir se o arquivo requirements.txt existe.

Se você executar o arquivo python, receberá algumas mensagens úteis sobre o que está acontecendo com o sistema operacional.

❯ python setup.py 
Python found in: /usr/bin/python3
Your venv .venv has been created
Requirements file "requirements.txt" found
Installing requirements
Collecting asgiref==3.3.4 .......
Process completed! Now activate your environment with "source .venv/bin/activate"

Observe que obtemos a saída do processo de instalação porque não estamos redirecionando a saída padrão para uma variável.

Execute outra linguagem de programação

Podemos executar outras linguagens de programação com python e obter a saída desses arquivos. Isso é possível porque os subprocessos interagem diretamente com o sistema operacional.

Por exemplo, vamos criar um programa hello world em C++ e Java. Para executar o seguinte arquivo, você precisará instalar C++ e Java compiladores.

olamundo.cpp

#include <iostream>

int main(){
    std::cout << "This is a hello world in C++" << std::endl;
    return 0;
}

olámundo.java

class HelloWorld{  
    public static void main(String args[]){  
     System.out.println("This is a hello world in Java");  
    }  
}  

Eu sei que isso parece muito código comparado a um simples one-liner do Python, mas isso é apenas para fins de teste.

Vamos criar um script Python que executa todos os arquivos C++ e Java em um diretório. Para fazer isso, primeiro queremos obter uma lista de arquivos dependendo da extensão do arquivo e glob permite-nos fazê-lo facilmente!

from glob import glob

# Gets files with each extension
java_files = glob('*.java')

cpp_files = glob('*.cpp')

Depois disso, podemos começar a usar subprocessos para executar cada tipo de arquivo.

for file in cpp_files:
    process = subprocess.run(f'g++ {file} -o out; ./out', shell=True, capture_output=True, text=True)
    
    output = process.stdout.strip() + ' BTW this was runned by Python'

    print(output)

for file in java_files:
    without_ext = file.strip('.java')
    process = subprocess.run(f'java {file}; java {without_ext}',shell=True, capture_output=True, text=True)

    output = process.stdout.strip() + ' A Python subprocess runned this :)'
    print(output)

Um pequeno truque é usar a faixa de função de string para modificar a saída e obter apenas o que precisamos.

Observação: tenha cuidado ao executar arquivos Java ou C++ grandes, pois estamos carregando a saída deles na memória e isso pode produzir um vazamento de memória.

Abra programas externos

Podemos executar outros programas apenas chamando a localização de seus binários por meio de um subprocesso.

Vamos experimentá-lo abrindo o Brave, meu navegador preferido.

import subprocess

subprocess.run('brave')

Isso abrirá uma instância do navegador ou apenas outra guia, se você já estiver executando o navegador.

Como em qualquer outro programa que aceite sinalizadores, podemos usá-los para produzir o comportamento desejado.

import subprocess

subprocess.run(['brave', '--incognito'])

Resumindo

Um subprocesso é um processo de computador criado por outro processo. Podemos verificar os processos que nosso computador está executando com ferramentas como htop e o gerenciador de tarefas.

Python tem sua própria biblioteca para trabalhar com subprocessos. Atualmente, a função run nos oferece uma interface simples para criar e gerenciar subprocessos.

Podemos criar qualquer tipo de aplicativo com eles porque estamos interagindo diretamente com o sistema operacional.

Por fim, lembre-se de que a melhor maneira de aprender é criar algo que você gostaria de usar.