Estoy usando Python subprocess.communicate()
para leer stdout de un proceso que se ejecuta durante aproximadamente un minuto.
¿Cómo puedo imprimir cada línea de ese proceso stdout
en forma de transmisión, de modo que pueda ver la salida a medida que se genera, pero aún bloquear la terminación del proceso antes de continuar?
subprocess.communicate()
parece dar toda la salida a la vez.
python
subprocess
Heinrich Schmetterling
fuente
fuente
Respuestas:
Tenga en cuenta que creo que el método de JF Sebastian (a continuación) es mejor.
Aquí hay un ejemplo simple (sin verificación de errores):
import subprocess proc = subprocess.Popen('ls', shell=True, stdout=subprocess.PIPE, ) while proc.poll() is None: output = proc.stdout.readline() print output,
Si
ls
termina demasiado rápido, entonces el ciclo while puede terminar antes de que haya leído todos los datos.Puede capturar el resto en stdout de esta manera:
output = proc.communicate()[0] print output,
fuente
while proc.poll() is None: time.sleep(0)
o algo por el estilo. Básicamente, debe asegurarse de que la nueva línea de salida sea lo último que haga el proceso (porque no puede darle tiempo al intérprete para que repita el ciclo) o debe hacer algo "elegante".Para obtener la salida del subproceso línea por línea tan pronto como el subproceso vacíe su búfer de salida estándar:
#!/usr/bin/env python2 from subprocess import Popen, PIPE p = Popen(["cmd", "arg1"], stdout=PIPE, bufsize=1) with p.stdout: for line in iter(p.stdout.readline, b''): print line, p.wait() # wait for the subprocess to exit
iter()
se usa para leer líneas tan pronto como se escriben para solucionar el error de lectura anticipada en Python 2 .Si el subproceso 'stdout usa un búfer de bloque en lugar de un búfer de línea en modo no interactivo (que conduce a un retraso en la salida hasta que el búfer del niño está lleno o vaciado explícitamente por el niño), entonces podría intentar forzar una salida sin búfer usando
pexpect
,pty
módulos ounbuffer
,stdbuf
,script
utilidades , ver Q: ¿Por qué no sólo tiene que utilizar un tubo (popen ())?Aquí está el código de Python 3:
#!/usr/bin/env python3 from subprocess import Popen, PIPE with Popen(["cmd", "arg1"], stdout=PIPE, bufsize=1, universal_newlines=True) as p: for line in p.stdout: print(line, end='')
Nota: A diferencia de Python 2, que genera cadenas de bytes de subprocesos tal cual; Python 3 usa el modo de texto (la salida de cmd se decodifica usando
locale.getpreferredencoding(False)
codificación).fuente
b''
es unbytes
literal en Python 2.7 y Python 3.bufsize=1
puede marcar la diferencia si también escribe (usandop.stdin
) en el subproceso, por ejemplo, puede ayudar a evitar un punto muerto mientras realiza unpexpect
intercambio interactivo ( como), asumiendo que no hay problemas de almacenamiento en búfer en el proceso hijo en sí. Si solo está leyendo, como dije, la diferencia está solo en el rendimiento: si no es así, ¿podría proporcionar un ejemplo de código completo mínimo que lo muestre?stderr=subprocess.STDOUT
aPopen()
). Consulte también las soluciones de subprocesamiento o asyncio vinculadas allí.stdout=PIPE
no captura la salida (todavía la ve en la pantalla), entonces su programa podría imprimir en stderr o directamente en la terminal. Para fusionar stdout y stderr, pasestderr=subprocess.STDOUT
(vea mi comentario anterior). Para capturar la salida impresa directamente en su tty, puede usar las soluciones pexpect, pty. . Aquí hay un ejemplo de código más complejo .Creo que la forma más sencilla de recopilar la salida de un proceso en forma de transmisión es así:
import sys from subprocess import * proc = Popen('ls', shell=True, stdout=PIPE) while True: data = proc.stdout.readline() # Alternatively proc.stdout.read(1024) if len(data) == 0: break sys.stdout.write(data) # sys.stdout.buffer.write(data) on Python 3.x
La función
readline()
oread()
solo debe devolver una cadena vacía en EOF, después de que el proceso haya terminado; de lo contrario, se bloqueará si no hay nada para leer (readline()
incluye la nueva línea, por lo que en líneas vacías, devuelve "\ n"). Esto evita la necesidad de una incómodacommunicate()
llamada final después del ciclo.En archivos con líneas muy largas
read()
puede ser preferible reducir el uso máximo de memoria; el número que se le pasa es arbitrario, pero excluirlo da como resultado la lectura de toda la salida de la tubería a la vez, lo que probablemente no sea deseable.fuente
data = proc.stdout.read()
bloques hasta que se lean todos los datos. Es posible que lo confunda conos.read(fd, maxsize)
que puede regresar antes (tan pronto como haya datos disponibles).read()
entonces funciona bien y tambiénreadline()
funciona bien siempre que la longitud máxima de la línea sea razonable. Actualicé mi respuesta en consecuencia.Si desea un enfoque sin bloqueo, no use
process.communicate()
. Si establece elsubprocess.Popen()
argumentostdout
enPIPE
, puede leerprocess.stdout
y verificar si el proceso aún se ejecuta usandoprocess.poll()
.fuente
Si simplemente está tratando de pasar la salida en tiempo real, es difícil hacerlo más simple que esto:
import subprocess # This will raise a CalledProcessError if the program return a nonzero code. # You can use call() instead if you don't care about that case. subprocess.check_call(['ls', '-l'])
Consulte los documentos de subprocess.check_call () .
Si necesitas procesar la salida, claro, haz un bucle. Pero si no lo hace, manténgalo simple.
Editar: JF Sebastian señala que los valores predeterminados para los parámetros stdout y stderr pasan a sys.stdout y sys.stderr, y que esto fallará si sys.stdout y sys.stderr han sido reemplazados (por ejemplo, para capturar la salida en pruebas).
fuente
sys.stdout
osys.stderr
son sustituidos por tipo fichero objetos que no tienen fileno real (). Sisys.stdout
,sys.stderr
no se sustituyen entonces es aún más simple:subprocess.check_call(args)
.call()
más acheck_call()
menos que quiera elCalledProcessError
.python -mthis
: "Los errores nunca deben pasar en silencio. A menos que se silencie explícitamente". es por eso que el código de ejemplo debe preferircheck_call()
máscall()
.call()
termino devuelven códigos de error distintos de cero en condiciones sin errores, porque son terribles. Entonces, en nuestro caso, un código de error distinto de cero no es realmente un error.grep
que pueden devolver un estado de salida distinto de cero incluso si no hay ningún error; son excepciones. Por defecto, el estado de salida cero indica éxito.myCommand="ls -l" cmd=myCommand.split() # "universal newline support" This will cause to interpret \n, \r\n and \r equally, each as a newline. p = subprocess.Popen(cmd, stderr=subprocess.PIPE, universal_newlines=True) while True: print(p.stderr.readline().rstrip('\r\n'))
fuente
shlex.split(myCommand)
lugar demyCommand.split()
. También respeta los espacios en los argumentos citados.Añadiendo otra solución de python3 con algunos pequeños cambios:
with
construcción)import subprocess import sys def subcall_stream(cmd, fail_on_error=True): # Run a shell command, streaming output to STDOUT in real time # Expects a list style command, e.g. `["docker", "pull", "ubuntu"]` p = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, bufsize=1, universal_newlines=True) for line in p.stdout: sys.stdout.write(line) p.wait() exit_code = p.returncode if exit_code != 0 and fail_on_error: raise RuntimeError(f"Shell command failed with exit code {exit_code}. Command: `{cmd}`") return(exit_code)
fuente