Introducción
En las tareas de operaciones, a menudo todavía necesita invocar comandos del sistema existentes aunque esté escribiendo código Python. Por eso es importante comprender las herramientas de Python para ejecutar procesos.
El módulo subprocess permite que Python inicie otros programas, capture su salida e inspeccione de forma segura si se ejecutaron correctamente o fallaron. Es la forma estándar de conectar el código Python con los comandos de shell.
En este laboratorio, aprenderá a:
- Importar el módulo
subprocess. - Ejecutar un comando sencillo de Linux (
ls -l) mediante Python. - Capturar la salida del comando para procesarla posteriormente.
- Decodificar la salida de bytes a una cadena legible.
- Gestionar correctamente los errores cuando falla un comando.
Trabajará en el directorio ~/project dentro de un entorno Linux.
Importar Subprocess
En este paso, creará un script de Python e importará el módulo necesario para interactuar con el shell del sistema.
El módulo subprocess forma parte de la biblioteca estándar de Python, por lo que no necesita instalar nada adicional. Permite que el script de Python se comunique con el sistema operativo subyacente.
Este es el puente estándar entre el código Python y las herramientas Unix existentes. En las tareas de operaciones, este puente es importante porque rara vez se reemplazan todos los comandos de shell de una sola vez. Lo habitual es que Python actúe como coordinador: invoca las herramientas del sistema, captura sus resultados y añade lógica de nivel superior.
Primero, asegúrese de encontrarse en el directorio correcto:
cd ~/project
Ahora, cree un archivo de Python llamado list_files.py mediante el editor nano o el método que prefiera:
nano list_files.py
Añada la siguiente línea al principio del archivo para importar el módulo:
import subprocess
print("Module imported successfully")
Guarde el archivo y salga del editor (en nano, pulse Ctrl+X, después Y y, por último, Enter).
Ahora, ejecute el script para comprobar que todo funciona correctamente:
python3 list_files.py
Debería ver el mensaje "Module imported successfully".
Ejecutar un comando
En este paso, modificará el script para ejecutar realmente un comando de Linux. Utilizaremos la función subprocess.run(), introducida en Python 3.5 como el enfoque recomendado para invocar subprocesos.
Cuando Python inicia otro programa, crea un proceso independiente. Ese proceso hijo tiene su propio código de salida, salida estándar y error estándar, igual que un comando que ejecuta directamente en el shell. Comprender este límite entre procesos facilita mucho el razonamiento sobre la gestión de errores posterior.
Abra de nuevo list_files.py:
nano list_files.py
Modifique el archivo para que tenga el siguiente contenido. Utilizaremos el comando ls -l para mostrar los archivos del directorio actual con información detallada:
import subprocess
## Run the 'ls -l' command
## The command and its arguments are passed as a list of strings
subprocess.run(["ls", "-l"])
Observe que pasamos el comando como una lista: ["ls", "-l"]. Esto es más seguro y claro que pasar una sola cadena, porque evita problemas relacionados con la inyección de shell y los espacios en los nombres de archivo.
Guarde el archivo y salga.
Ahora, ejecute el script:
python3 list_files.py
Debería ver directamente en el terminal la salida del comando ls -l. Esto sucede porque, de forma predeterminada, subprocess.run envía la salida del comando a la salida estándar, es decir, a la pantalla.
Capturar la salida
En el paso anterior, la salida se mostró directamente en la pantalla. Sin embargo, en los scripts de automatización normalmente querrá capturar esa salida en una variable para que el código Python pueda procesarla, por ejemplo, analizar nombres de archivo o comprobar si hay errores.
Esta es la diferencia entre limitarse a ejecutar un comando e integrarlo en un programa. Una vez capturada la salida como datos, el script puede tomar decisiones basadas en ella, en lugar de dejar el resultado únicamente en el terminal para que lo lea una persona.
Para hacerlo, debemos utilizar el argumento capture_output=True.
Abra de nuevo list_files.py:
nano list_files.py
Actualice el código para capturar el resultado:
import subprocess
## Run the command and capture the output
result = subprocess.run(["ls", "-l"], capture_output=True)
## Print the captured result object
print(result)
Guarde el archivo y salga.
Ejecute el script:
python3 list_files.py
Verá una salida similar a la siguiente:
CompletedProcess(args=['ls', '-l'], returncode=0, stdout=b'total 0\n-rw-rw-r-- 1 labex labex 0 ...', stderr=b'')
Este es un objeto CompletedProcess. Observe la parte stdout=b'...'. La b representa bytes. Esto significa que Python ha capturado los datos sin procesar exactamente como los ve el equipo, no como texto legible (una cadena). En el siguiente paso convertiremos estos datos en texto legible.
Decodificar la salida
Como la salida se captura como bytes, debemos decodificarla en una cadena estándar de Python (UTF-8) para que sea legible y utilizable.
La diferencia entre bytes y texto es fundamental en Python. En última instancia, los equipos intercambian bytes sin procesar, mientras que las personas normalmente necesitan texto. Muchos errores confusos en la automatización se producen por olvidar en qué formato están los datos en ese momento.
Abra list_files.py una vez más:
nano list_files.py
Actualice el código para acceder al atributo stdout y decodificarlo:
import subprocess
## Run the command and capture the output
result = subprocess.run(["ls", "-l"], capture_output=True)
## Decode the bytes to a string
output_string = result.stdout.decode("utf-8")
## Print the clean string
print("--- Command Output ---")
print(output_string)
Guarde el archivo y salga.
Ejecute el script:
python3 list_files.py
Ahora debería ver la salida limpia y con el formato del comando ls -l, igual que si lo hubiera ejecutado directamente en el shell, pero en este caso la muestra Python.
Nota: Como alternativa, puede pasar text=True (o universal_newlines=True) a subprocess.run() para que Python gestione automáticamente la decodificación. No obstante, es importante comprender el método .decode() para trabajar con datos binarios.
Gestionar errores
¿Qué sucede si falla el comando? Por ejemplo, si intenta mostrar un directorio que no existe. Los scripts correctos deben gestionar estos errores.
En los sistemas similares a Unix, los fallos de los comandos suelen comunicarse mediante un estado de salida distinto de cero. subprocess.run(..., check=True) convierte esta convención del sistema operativo en una excepción de Python, lo que permite que el script reaccione mediante el flujo de control normal de Python.
Creemos un nuevo script llamado handle_error.py para demostrarlo.
nano handle_error.py
Añada el siguiente código. Intentaremos mostrar un directorio inexistente llamado /nonexistent. También utilizamos check=True, que indica a Python que lance una excepción CalledProcessError si el comando devuelve un código de salida distinto de cero, lo que indica un fallo.
import subprocess
try:
## Try to list a directory that does not exist
## check=True raises an exception if the command fails
subprocess.run(["ls", "/nonexistent"], check=True, capture_output=True)
except subprocess.CalledProcessError as e:
print("An error occurred!")
print(f"Return code: {e.returncode}")
## We can also print the error message generated by the command (stderr)
print(f"Error message: {e.stderr.decode('utf-8')}")
Guarde el archivo y salga.
Ejecute el script:
python3 handle_error.py
Debería ver una salida que indique que el error se capturó:
An error occurred!
Return code: 2
Error message: ls: cannot access '/nonexistent': No such file or directory
Si no utilizáramos el bloque try-except, el programa se detendría y mostraría un traceback desordenado. Este enfoque permite que el script detecte los fallos y decida qué hacer a continuación, por ejemplo, registrar el error, volver a intentarlo o salir correctamente.
Resumen
En este laboratorio, ha aprendido los fundamentos de la ejecución de comandos de shell mediante el módulo subprocess de Python. Ha practicado cómo:
- Importar el módulo
subprocess. - Ejecutar comandos mediante
subprocess.run(). - Capturar la salida estándar mediante
capture_output=True. - Decodificar la salida en bytes para convertirla en cadenas legibles.
- Gestionar fallos de comandos mediante
check=Truey bloquestry-except.
Estas habilidades son esenciales para crear scripts de automatización, herramientas de implementación y utilidades de administración de sistemas en Python.



