HOWTO · Python
Cómo ejecutar scripts de Bash en Python
Aprende a ejecutar scripts de Bash usando Python con nuestra guía integral. Explora métodos como subprocess, os.system y subprocess.Popen para ejecutar comandos de Bash de manera eficiente. Mejora tus habilidades de automatización y optimiza los flujos de trabajo al integrar scripts de Bash en tus aplicaciones de Python.
En esta página
Usa subprocess.run para la mayoría de los scripts de Bash que deban terminar antes de que Python continúe. Permite pasar los argumentos por separado, capturar stdout y stderr y comprobar el returncode del proceso. Bash debe estar instalado y disponible para Python, por lo que este enfoque depende del entorno anfitrión y no es portable a sistemas que no tengan Bash. El ejemplo crea su propio archivo de script, pasa un nombre como argumento y muestra el resultado capturado.
Método 1: Usar el módulo subprocess
Este ejemplo autónomo crea un pequeño script de Bash, le pasa un argumento y captura ambos flujos de salida. El script de Bash imprime un saludo; luego Python muestra el texto capturado y el estado del proceso. En el ejemplo sin cambios, el saludo y la etiqueta «Return code» que aparecen en pantalla permanecen en inglés.
import subprocess
import tempfile
from pathlib import Path
with tempfile.TemporaryDirectory() as directory:
script_path = Path(directory) / "greet.sh"
script_path.write_text(
'#!/usr/bin/env bash\nprintf "Hello, %s!\\n" "$1"\n',
encoding="utf-8",
)
result = subprocess.run(
["bash", str(script_path), "Ada"],
capture_output=True,
text=True,
check=False,
)
print(result.stdout, end="")
print(f"Return code: {result.returncode}")
Hello, Ada!
Return code: 0
La lista de argumentos mantiene la ruta del script y Ada como argumentos separados. Con el valor predeterminado shell=False, Python inicia Bash directamente, sin pedirle a un shell que interprete la lista. Así se evita que un shell analice esos argumentos, pero no es una garantía de seguridad universal: valida las entradas y ten en cuenta lo que hace el propio script. Un returncode igual a cero indica que el proceso terminó correctamente; un valor distinto de cero indica que informó de un fallo. stderr es un flujo independiente para los diagnósticos, y que aparezca texto en él no significa por sí solo que el comando haya fallado. capture_output=True deja stdout y stderr disponibles en el objeto de resultado.
Método 2: Usar os.system
Usa os.system solo cuando baste con un comando sencillo y Python no necesite capturar su salida. Acepta una única cadena de comando, la ejecuta mediante un shell del sistema y envía los flujos del comando a los flujos estándar del proceso que lo invoca. Su valor de retorno depende del sistema y puede codificar el estado del proceso en vez de coincidir con el código de salida del proceso hijo; por eso, no lo trates como un código de salida directo y portable. No proporciona un objeto de resultado con stdout o stderr. Evita construir la cadena de comando con datos no confiables, porque el shell puede darles un significado especial. Para pasar argumentos por separado, capturar flujos e inspeccionar el estado de forma fiable, prefiere subprocess.
Método 3: Usar subprocess.Popen
Elige subprocess.Popen cuando necesites un control de nivel más bajo sobre un proceso en ejecución, por ejemplo, para gestionar su ciclo de vida o conectar tuberías. Para un intercambio finito, configura stdin=subprocess.PIPE al crear el proceso y luego llama a communicate(input=...) para enviar datos y recoger los flujos capturados cuando el proceso termine. Sin esa tubería de entrada, communicate(input=...) no puede enviar datos al proceso hijo. La salida no se entrega en tiempo real. Si creas tuberías para ambos flujos de salida, drénalas con communicate(); esperar a que termine un proceso hijo mientras su tubería se llena puede provocar un interbloqueo. Para transmitir la salida en directo, lee y drena los flujos de forma deliberada, en lugar de tratar communicate() como una API de streaming. El comportamiento exacto de las tuberías depende de la plataforma y del proceso.
Conclusión
Para un script de Bash que se ejecuta hasta terminar, subprocess.run suele ser la opción más clara: pasa una lista, captura los flujos cuando sea necesario y usa returncode para determinar si hubo éxito. Reserva os.system para comandos de shell sencillos que no requieran gestionar la salida, y usa subprocess.Popen cuando necesites controlar o comunicarte con el proceso de forma explícita. Comprueba que Bash esté instalado y disponible en el entorno donde se ejecuta el programa Python; el script solo podrá iniciarse si Python encuentra el ejecutable de Bash.
Preguntas frecuentes
-
¿Cuál es la mejor forma de ejecutar un script de Bash desde Python?
Usasubprocess.runpara la mayoría de los scripts que terminan antes de que Python deba continuar. Admite listas de argumentos, captura de flujos y consulta del estado del proceso mediantereturncode. -
¿Cómo puedo capturar la salida de un script?
Configuracapture_output=Trueytext=Trueensubprocess.runy luego leestdoutystderrdel objeto devuelto. ConPopen, conecta las tuberías necesarias y llama acommunicate()para recoger los flujos una vez finalizado el proceso; esto recopila la salida, pero no la transmite en directo.stderrpuede contener diagnósticos aunque el proceso tenga éxito, así que usareturncodepara comprobar su estado. -
¿Es seguro pasar una lista?
Conshell=False, una lista evita que un shell interprete los argumentos proporcionados. Esto reduce el riesgo de inyección de comandos, pero no hace que cualquier script o entrada sea segura: valida los valores y ten en cuenta el comportamiento del script. -
¿Puedo pasar argumentos a un script de Bash?
Sí. Añade cada argumento como un elemento independiente después de la ruta del script en la lista de argumentos desubprocess.os.systemrecibe una sola cadena de comando, por lo que no ofrece el mismo mecanismo estructurado para pasar argumentos. -
¿Cómo puedo proporcionar datos de entrada al script?
Usasubprocess.runconinput=...para un intercambio finito y sencillo, o usaPopenconstdin=subprocess.PIPEycommunicate(input=...)cuando necesites un control de nivel más bajo.communicate()envía los datos de entrada y recoge los flujos del proceso cuando termina el hijo.