El autor seleccionó el COVID-19 Relief Fund para que reciba una donación como parte del programa Write for DOnations.
Python 3 incluye el módulo subprocess
para ejecutar programas externos y leer sus resultados en su código Python.
Encontrará subprocess
útil si desea usar otro programa en su computadora desde su código Python. Por ejemplo, es posible que desee invocar git
desde su código Python para recuperar archivos en su proyecto que tienen un seguimiento por parte del control de versiones de git
. Ya que cualquier programa al que acceda en su computadora puede estar controlado por subprocess
, los ejemplos mostrados aquí serán aplicables a cualquier programa externo que desee invocar desde su código Python.
subprocess
incluye varias clases y funciones, pero en este tutorial cubriremos una de las funciones más útiles de subprocess
: subprocess.run
. Revisaremos sus diferentes usos y principales argumentos de palabras clave.
Para sacar el máximo provecho de este tutorial, se recomienda tener cierta familiaridad con la programación en Python 3. Puede consultar estos tutoriales para obtener la información de fondo necesaria:
Puede usar la función subprocess.run
para ejecutar un programa externo desde su código Python. Primero, sin embargo, necesitará importar los módulos subprocess
y sys
a su programa:
import subprocess
import sys
result = subprocess.run([sys.executable, "-c", "print('ocean')"])
Si ejecuta esto, recibirá un resultado similar al siguiente:
Outputocean
Vamos a revisar este ejemplo:
sys.executable
es la ruta absoluta al ejecutable de Python con el que se invocó su programa en un principio. Por ejemplo, sys.executable
podría ser una ruta como /usr/local/bin/python
.subprocess.run
obtiene una lista de cadenas que consisten en los componentes del comando que estamos intentando ejecutar. Ya que la primera cadena que pasamos es sys.executable
, indicamos a subprocess.run
que ejecute un nuevo programa Python.-c
es una opción de línea de comandos python
que le permite pasar una cadena con un programa completo Python para ejecutar. En nuestro caso, pasamos un programa que imprime la cadena ocean
.Puede pensar en cada entrada de la lista que pasamos a subprocess.run
como si estuviese separada por un espacio. Por ejemplo, [sys.executable, "-c", "print('ocean')"]
se traduce aproximadamente como /usr/local/bin/python -c "print('ocean')"
. Observe que subprocess
automáticamente pone entre comillas los componentes del comando antes de intentar ejecutarlos sobre el sistema operativo subyacente de forma que, por ejemplo, puede pasar un nombre de archivo que tenga espacios.
Advertencia: Nunca pase una entrada no confiable a subprocess.run
. Ya que subprocess.run
tiene la capacidad de realizar comandos arbitrarios en su computadora, los agentes maliciosos pueden usarlo para manipular su computadora de formas inesperadas.
Ahora que podemos invocar un programa externo usando subprocess.run
, vamos a ver cómo podemos capturar el resultado desde ese programa. Por ejemplo, este proceso podría ser útil si quisiéramos usar git ls-files
para crear todos sus archivos guardados actualmente bajo control de versiones.
Nota: Los ejemplos que se muestran en esta sección requieren Python 3.7 o posterior. En particular, los argumentos de palabra clave capture_output
y text
fueron añadidos en Python 3.7 cuando se lanzó en junio del 2018.
Vamos a añadir nuestro ejemplo 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)
Si ejecutamos este código, obtendremos un resultado como el siguiente:
Outputstdout: ocean
stderr:
Este ejemplo es, en gran medida, el mismo que presentamos en la primera sección: aún estamos ejecutando un subproceso para imprimir ocean
. Es sobre todo importante que pasemos los argumentos de palabra clave capture_output=True
y text=True
a subproecss.run
.
subprocess-run
devuelve un objeto subprocess.CompletedProcess
que está vinculado a result
. El objeto subprocess.CompletedProcess
incluye detalles sobre el código de salida del programa externo y su resultado. capture_output=True
garantiza que result-stdout
y result-stderr
se completan con el resultado correspondiente del programa externo. De forma predeterminada, result.stdout
y result.stderr
están vinculados como bytes, pero el argumento de palabra clave text=True
indica a Python que decodifique los bytes en cadenas.
En la sección de resultado, stdout
es ocean
(además de la nueva línea final que print
añade de forma implícita) y no tenemos stderr
.
Vamos a probar un ejemplo que produce un valor no vacío 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)
Si ejecutamos este código, obtendremos un resultado similar al siguiente:
Outputstdout:
stderr: Traceback (most recent call last):
File "<string>", line 1, in <module>
ValueError: oops
Este código ejecuta un subproceso de Python que crea de inmediato un ValueError
. Cuando inspeccionamos el result
ado final, no vemos nada en stdout
y un Traceback
de nuestro ValueError
en stderr
. Esto es porque, por defecto, Python escribe el Traceback
de la excepción sin administrar a stderr
.
A veces, es útil generar una excepción si un programa que ejecutamos sale con un código de salida erróneo. Los programas que salen con un código cero se consideran correctos, pero se considera que los programas que salen con un código no cero encontraron un error. Como ejemplo, este patrón podría ser útil si quisiéramos generar una excepción en el caso de que ejecutemos git ls-files
es un directorio que realmente no era un repositorio git
.
Podemos usar el argumento de palabra clave check=True
para que subprocess.run
genere una excepción si el programa externo devuelve un código de salida no cero:
import subprocess
import sys
result = subprocess.run([sys.executable, "-c", "raise ValueError('oops')"], check=True)
Si ejecutamos este código, obtendremos un resultado similar al siguiente:
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.
Este resultado muestra que ejecutamos un subproceso que generó un error, que se imprime en stderr
en nuestro terminal. A continuación, subprocess.run
planteó generó un subprocess.CalledProcessError
en nuestro nombre en nuestro programa Python principal.
Alternativamente, el módulo subprocess
también incluye el método subprocess.CompletedProcess.check_returncode
, que podemos invocar a efectos similares:
import subprocess
import sys
result = subprocess.run([sys.executable, "-c", "raise ValueError('oops')"])
result.check_returncode()
Si ejecutamos este código, recibiremos:
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.
Ya que no pasamos check=True
a subprocess.run
, vinculamos correctamente una instancia subprocess.CompletedProcess
a result
, a pesar de que nuestro programa salió con un código no cero. Al invocar result.check_returncode()
, sin embargo, se genera un subprocess.CalledProcessError
porque detecta que el proceso completado salió con un código erróneo.
subprocess.run
incluye el argumento timeout
para permitirle detener un programa externo si tarda demasiado en ejecutarse:
import subprocess
import sys
result = subprocess.run([sys.executable, "-c", "import time; time.sleep(2)"], timeout=1)
Si ejecutamos este código, obtendremos un resultado como el siguiente:
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
El subproceso que intentamos ejecutar usó la función time.sleep
para hibernar durante 2
segundos. Sin embargo, pasamos el argumento de palabra clave timeout=1
a subprocess.run
para desconectar nuestro subproceso tras 1
segundo. Esto explica por qué nuestra invocación a subprocess.run
finalmente generó una excepción subprocess.TimeoutExpired
.
Tenga en cuenta que el argumento de palabra clave timeout
para subprocess.run
es aproximado. Python hará todo lo posible para anular el subproceso tras el número de segundos de timeout
, pero no será necesariamente exacto.
A veces, los programas esperan que se pase una entrada a través de stdin
.
El argumento de palabra clave input
a subprocess.run
le permite pasar datos al stdin
del subproceso. Por ejemplo:
import subprocess
import sys
result = subprocess.run(
[sys.executable, "-c", "import sys; print(sys.stdin.read())"], input=b"underwater"
)
Obtendremos un resultado similar al siguiente tras ejecutar este código:
Outputunderwater
En este caso, pasamos los bytes underwater
a input
. Nuestro subproceso objetivo utilizó sys.stdin
para leer lo pasado en stdin
(underwater
) e imprimió nuestro resultado.
El argumento de palabra clave input
puede ser útil si desea encadenar múltiples invocaciones subprocess.run
juntas pasando el resultado de un programa como la entrada de otro.
El módulo subprocess
es una parte potente de la biblioteca estándar de Python que le permite ejecutar programas externos e inspeccionar sus resultados fácilmente. En este tutorial, aprendió a usar subprocess.run
para controlar programas externos, pasar entrada a ellos, analizar sus resultados y comprobar sus códigos de retorno.
El módulo subprocess
expone clases y utilidades adicionales que no abarcamos en este tutorial. Ahora que tiene una base, puede usar la documentación del módulo subprocess
para obtener más información sobre otras clases y utilidades disponibles.
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!