O autor selecionou a COVID-19 Relief Fund para receber uma doação como parte do programa Write for DOnations.
O Python 3 inclui o módulo subprocess
para executar programas externos e ler suas saídas em seu código Python.
Você pode achar o subprocess
útil se quiser usar outro programa em seu computador dentro do seu código Python. Por exemplo, você pode querer invocar o git
dentro do seu código Python para recuperar arquivos em seu projeto que são rastreados no controle de versão git
. Como qualquer programa que você possa acessar em seu computador pode ser controlado pelo subprocess
, os exemplos mostrados aqui serão aplicáveis a qualquer programa externo que você queira invocar a partir do seu código Python.
O subprocess
inclui várias classes e funções, mas neste tutorial, vamos cobrir uma das funções mais úteis do subprocess
: subprocess.run
. Vamos revisar seus diferentes usos e principais argumentos de palavra-chave.
Para tirar o máximo proveito deste tutorial, recomenda-se ter alguma familiaridade com programação em Python 3. Você pode revisar esses tutoriais para as informações básicas necessárias:
Você pode usar a função subprocess.run
para executar um programa externo a partir do seu código Python. Primeiro, porém, você precisa importar os módulos subprocess
e sys
para seu programa:
import subprocess
import sys
result = subprocess.run([sys.executable, "-c", "print('ocean')"])
Se você executar isso, receberá uma saída como a seguinte:
Outputocean
Vamos revisar este exemplo:
sys.executable
é o caminho absoluto para o executável Python com o qual seu programa foi originalmente invocado. Por exemplo, sys.executable
pode ser um caminho como /usr/local/bin/python
.subprocess.run
recebe uma lista de strings que consistem nos componentes do comando que estamos tentando executar. Como a primeira string que passamos é sys.executable
, estamos instruindo subprocess.run
a executar um novo programa Python.-c
é uma opção de linha de comando python
que lhe permite passar uma string com um programa Python inteiro para executar. Em nosso caso, passamos um programa que imprime a string ocean
.Você pode pensar em cada entrada na lista que passamos para subprocess.run
como sendo separadas por um espaço. Por exemplo, [sys.executable, "-c", "print('ocean')"]
traduz aproximadamente para /usr/local/bin/python -c "print('ocean')"
. Observe que o subprocess
automaticamente coloca aspas nos componentes do comando antes de tentar executá-los no sistema operacional subjacente para que, por exemplo, você possa passar um nome de arquivo que tenha espaços nele.
Atenção: nunca passe entrada não confiável para subprocess.run
. Como o subprocess.run
tem a capacidade de executar comandos arbitrários em seu computador, indivíduos maliciosos podem usá-lo para manipular seu computador de maneiras inesperadas.
Agora que podemos invocar um programa externo usando subprocess.run
, vamos ver como podemos capturar a saída desse programa. Por exemplo, este processo poderia ser útil se quiséssemos usar git ls-files
para exibir todos os seus arquivos atualmente armazenados sob o controle de versão.
Nota: os exemplos mostrados nesta seção exigem Python 3.7 ou superior. Em particular, os argumentos de palavra-chave capture_output
e text
foram adicionados ao Python 3.7 quando ele foi lançado em junho de 2018.
Vamos adicionar ao nosso exemplo anterior:
import subprocess
import sys
result = subprocess.run(
[sys.executable, "-c", "print('ocean')"], capture_output=True, text=True
)
print("stdout:", result.stdout)
print("stderr:", result.stderr)
Se executarmos esse código, receberemos um resultado como o seguinte:
Outputstdout: ocean
stderr:
Este exemplo é em grande parte o mesmo que o introduzido na primeira seção: ainda estamos executando um subprocesso para imprimir ocean
. É importante ressaltar, no entanto, que passamos os argumentos de palavra-chave capture_output=True
e text=True
para subprocess.run
.
subprocess.run
retorna um objeto subprocess.CompletedProcess
que está vinculado a result
. O objeto subprocess.CompletedProcess
inclui detalhes sobre o código de saída do programa externo e sua saída. capture_output=True
garante que result.stdout
e result.stderr
estejam preenchidos com a saída correspondente do programa externo. Por padrão, result.stdout
e result.stderr
são vinculados como bytes, mas o argumento de palavra-chave text=True
instrui o Python a, em vez disso, decodificar os bytes em strings.
Na seção output stdout
é ocean
(mais uma nova linha final que print
adiciona implicitamente), e não temos nenhum stderr
.
Vamos tentar um exemplo que produz um valor não vazio para stderr
:
import subprocess
import sys
result = subprocess.run(
[sys.executable, "-c", "raise ValueError('oops')"], capture_output=True, text=True
)
print("stdout:", result.stdout)
print("stderr:", result.stderr)
Se executarmos esse código, receberemos uma saída como a seguinte:
Outputstdout:
stderr: Traceback (most recent call last):
File "<string>", line 1, in <module>
ValueError: oops
Este código executa um subprocesso Python que imediatamente gera um erro de ValueError
. Quando inspecionamos o result
final, não vemos nada em stdout
e um Traceback
de nosso ValueError
em stderr
. Isso ocorre porque, por padrão, o Python escreve o Traceback
da exceção não tratada em stderr
.
Às vezes, é útil gerar uma exceção se um programa que executamos termine com um código de saída ruim. Os programas que terminam com um código zero são considerados bem sucedidos, mas programas que terminam com um código não zero são considerados como tendo encontrado um erro. Como um exemplo, esse padrão poderia ser útil se quiséssemos gerar uma exceção no caso em que executamos git ls-files
em um diretório que não fosse realmente um repositório git
.
Podemos usar o argumento de palavra-chave check=True
para subprocess.run
para ter uma exceção gerada se o programa externo retornar um código de saída não zero:
import subprocess
import sys
result = subprocess.run([sys.executable, "-c", "raise ValueError('oops')"], check=True)
Se executarmos esse código, receberemos uma saída como a seguinte:
OutputTraceback (most recent call last):
File "<string>", line 1, in <module>
ValueError: oops
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "/usr/local/lib/python3.8/subprocess.py", line 512, in run
raise CalledProcessError(retcode, process.args,
subprocess.CalledProcessError: Command '['/usr/local/bin/python', '-c', "raise ValueError('oops')"]' returned non-zero exit status 1.
Esta saída mostra que executamos um subprocesso que gerou um erro, que é impresso em stderr
em nosso terminal. Em seguida, subprocess.run
criou devidamente um subprocess.CalledProcessError
por nós em nosso programa Python principal.
Alternativamente, o módulo subprocess
também inclui o método subprocess.CompletedProcess.check_returncode
, que podemos invocar para um efeito semelhante:
import subprocess
import sys
result = subprocess.run([sys.executable, "-c", "raise ValueError('oops')"])
result.check_returncode()
Se executarmos este código, receberemos:
OutputTraceback (most recent call last):
File "<string>", line 1, in <module>
ValueError: oops
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "/usr/local/lib/python3.8/subprocess.py", line 444, in check_returncode
raise CalledProcessError(self.returncode, self.args, self.stdout,
subprocess.CalledProcessError: Command '['/usr/local/bin/python', '-c', "raise ValueError('oops')"]' returned non-zero exit status 1.
Como não passamos check=True
para subprocess.run
, vinculamos com sucesso uma instância de subprocess.CompletedProcess
a result
, mesmo que nosso programa seja encerrado com um código diferente de zero. Chamar result.check_returncode()
, no entanto, gera um subprocess.CalledProcessError
porque ele detecta que o processo finalizado foi encerrado com um código ruim.
subprocess.run
inclui o argumento timeout
para permitir que você pare um programa externo se estiver levando muito tempo para executar:
import subprocess
import sys
result = subprocess.run([sys.executable, "-c", "import time; time.sleep(2)"], timeout=1)
Se executarmos esse código, receberemos um resultado como o seguinte:
OutputTraceback (most recent call last):
File "<stdin>", line 1, in <module>
File "/usr/local/lib/python3.8/subprocess.py", line 491, in run
stdout, stderr = process.communicate(input, timeout=timeout)
File "/usr/local/lib/python3.8/subprocess.py", line 1024, in communicate
stdout, stderr = self._communicate(input, endtime, timeout)
File "/usr/local/lib/python3.8/subprocess.py", line 1892, in _communicate
self.wait(timeout=self._remaining_time(endtime))
File "/usr/local/lib/python3.8/subprocess.py", line 1079, in wait
return self._wait(timeout=timeout)
File "/usr/local/lib/python3.8/subprocess.py", line 1796, in _wait
raise TimeoutExpired(self.args, timeout)
subprocess.TimeoutExpired: Command '['/usr/local/bin/python', '-c', 'import time; time.sleep(2)']' timed out after 0.9997982999999522 seconds
O subprocesso que tentamos executar usou a função time.sleep
para colocá-lo em repouso por 2
segundos. No entanto, passamos o argumento de palavra-chave timeout=1
para subprocess.run
para expirar nosso subprocesso após 1
segundo. Isso explica por que nossa chamada para subprocess.run
finalmente gerou uma exceção subprocess.TimeoutExpired
.
Observe que o argumento de palavra-chave timeout
para subprocess.run
é aproximado. Python fará um melhor esforço para matar o subprocesso após o número de segundos de timeout
, mas ele não será necessariamente exato.
Às vezes, os programas esperam que a entrada seja passada para eles via stdin
.
O argumento de palavra-chave input
para subprocess.run
permite que você passe dados para o stdin
do subprocesso. Por exemplo:
import subprocess
import sys
result = subprocess.run(
[sys.executable, "-c", "import sys; print(sys.stdin.read())"], input=b"underwater"
)
Receberemos uma saída como a seguinte depois de executar este código:
Outputunderwater
Neste caso, passamos os bytes underwater
para input
. Nosso subprocesso alvo usou sys.stdin
para ler o que foi passado em stdin
(underwater
) e o imprimiu em nossa saída.
O argumento de palavra-chave input
pode ser útil se você quiser encadear várias chamadas subprocess.run
juntas passando a saída de um programa como a entrada para outro.
O módulo subprocess
é uma parte poderosa da biblioteca padrão Python que permite que você execute programas externos e inspecione suas saídas com facilidade. Neste tutorial, você aprendeu a usar subprocess.run
para controlar programas externos, passar a entrada para eles, analisar sua saída e verificar seus códigos de retorno.
O módulo subprocess
expõe classes e utilitários adicionais que não abordamos neste tutorial. Agora que você tem um conhecimento básico, você pode usar a documentação do módulo subprocess
para aprender mais sobre outras classes e utilitários disponíveis.
Thanks for learning with the DigitalOcean Community. Check out our offerings for compute, storage, networking, and managed databases.
This textbox defaults to using Markdown to format your answer.
You can type !ref in this text area to quickly search our full set of tutorials, documentation & marketplace offerings and insert the link!