Para gestionar subprocesos en Python, se emplea el módulo subprocess. Una tarea común es combinar la salida estándar (stdout) y la salida de error (stderr) de un subproceso, pero conservando su distinción para un análisis posterior. Esto se puede lograr mediante el método communicate() del objeto Popen, que facilita la captura separada de ambos flujos.
En el primer ejemplo, conifguramos un subproceso con tuberías independientes para stdout y stderrr, lo que permite su captura diferenciada:
import subprocess
# Iniciar un subproceso ejecutando un comando simple
proceso = subprocess.Popen(['echo', 'Ejemplo de ejecución'], stdout=subprocess.PIPE, stderr=subprocess.PIPE)
# Bloquear hasta que el subproceso termine y obtener las salidas
salida_std, salida_err = proceso.communicate()
# Decodificar y mostrar las salidas por separado
print("Contenido de stdout:")
print(salida_std.decode('utf-8'))
print("Contenido de stderr:")
print(salida_err.decode('utf-8'))
Si se desea unificar ambas salidas en un solo flujo, se puede redirigir stderr a stdout durante la configuración del subproceso:
import subprocess
# Configurar el subproceso para mezclar stderr con stdout
proceso = subprocess.Popen(['cat', '/ruta/no/existente'], stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
# Capturar la salida combinada
resultado, _ = proceso.communicate()
# Mostrar la salida unificada
print("Salida combinada:")
print(resultado.decode('utf-8'))
Para distinguir entre stdout y stderr después de la unificación, se puede implementar un mecanismo de etiquetado, como prefijos en los mensajes. Por ejemplo, redirigir stderr a stdout y luego parsear el contenido basado en marcadores predefinidos:
import subprocess
# Ejecutar un comando que produce salidas en ambos flujos
script = 'import sys; print("Mensaje estándar"); print("ERROR: Fallo en operación", file=sys.stderr)'
proceso = subprocess.Popen(['python', '-c', script], stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
# Obtener la salida total
texto, _ = proceso.communicate()
# Dividir la salida en líneas y clasificarlas
lineas = texto.decode('utf-8').splitlines()
salida_principal = []
errores = []
for linea in lineas:
if linea.startswith("ERROR:"): # Identificar líneas de error por un prefijo
errores.append(linea)
else:
salida_principal.append(linea)
print("Salida estándar extraída:")
print('\n'.join(salida_principal))
print("Errores identificados:")
print('\n'.join(errores))
Es importante considerar que para subprocesos con grandes volúmenes de salida, el uso de communicate() puede consumir mucha memoria. En tales casos, es recomendable alternar a métodos de lectura no bloqueante o I/O asíncrono para procesar la salida de manera incremental.