example español python subprocess stdout

python - español - captura de stdout en tiempo real del subproceso



subprocess python 3 example (11)

Quiero subprocess.Popen() rsync.exe en Windows, e imprimir el stdout en Python.

Mi código funciona, pero no capta el progreso hasta que se realiza una transferencia de archivos. Quiero imprimir el progreso de cada archivo en tiempo real.

Usar Python 3.1 ahora desde que lo escuché debería ser mejor en el manejo de IO.

import subprocess, time, os, sys cmd = "rsync.exe -vaz -P source/ dest/" p, line = True, ''start'' p = subprocess.Popen(cmd, shell=True, bufsize=64, stdin=subprocess.PIPE, stderr=subprocess.PIPE, stdout=subprocess.PIPE) for line in p.stdout: print(">>> " + str(line.rstrip())) p.stdout.flush()


Algunas reglas generales para el subprocess .

  • Nunca use shell=True . Invoca innecesariamente un proceso de shell adicional para llamar a su programa.
  • Al llamar a los procesos, los argumentos se pasan como listas. sys.argv en python es una lista, y también lo es argv en C. Entonces pasa una lista a Popen para llamar a subprocesos, no a una cadena.
  • No redirija el stderr a un PIPE cuando no lo esté leyendo.
  • No redirija el stdin cuando no está escribiendo.

Ejemplo:

import subprocess, time, os, sys cmd = ["rsync.exe", "-vaz", "-P", "source/" ,"dest/"] p = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) for line in iter(p.stdout.readline, b''''): print(">>> " + line.rstrip())

Dicho esto, es probable que rsync proteja su salida cuando detecta que está conectada a un conducto en lugar de a un terminal. Este es el comportamiento predeterminado: cuando se conecta a un conducto, los programas deben eliminar explícitamente stdout para obtener resultados en tiempo real, de lo contrario, la biblioteca C estándar almacenará en búfer.

Para probarlo, intente ejecutar esto en su lugar:

cmd = [sys.executable, ''test_out.py'']

y crea un archivo test_out.py con los contenidos:

import sys import time print ("Hello") sys.stdout.flush() time.sleep(10) print ("World")

Ejecutar ese subproceso debería darle "Hola" y esperar 10 segundos antes de dar "Mundo". Si eso ocurre con el código python anterior y no con rsync , eso significa que rsync sí mismo está almacenando en búfer, por lo que no tiene suerte.

Una solución sería conectarse directamente a una pty , usando algo como pexpect .


Cambie el stdout del proceso rsync para ser sin búfer.

p = subprocess.Popen(cmd, shell=True, bufsize=0, # 0=unbuffered, 1=line-buffered, else buffer-size stdin=subprocess.PIPE, stderr=subprocess.PIPE, stdout=subprocess.PIPE)


En Linux, tuve el mismo problema de deshacerme del almacenamiento en búfer. Finalmente utilicé "stdbuf -o0" (o, unbuffer from expect) para eliminar el buffer de PIPE.

proc = Popen([''stdbuf'', ''-o0''] + cmd, stdout=PIPE, stderr=PIPE) stdout = proc.stdout

Podría usar select.select en stdout.

Ver también https://unix.stackexchange.com/questions/25372/


Me di cuenta de que no se menciona el uso de un archivo temporal como intermedio. Lo siguiente soluciona los problemas de almacenamiento en búfer al enviar a un archivo temporal y le permite analizar los datos provenientes de rsync sin conectarse a un pty. Probé lo siguiente en un cuadro de Linux, y la salida de rsync tiende a diferir entre plataformas, por lo que las expresiones regulares para analizar el resultado pueden variar:

import subprocess, time, tempfile, re pipe_output, file_name = tempfile.TemporaryFile() cmd = ["rsync", "-vaz", "-P", "/src/" ,"/dest"] p = subprocess.Popen(cmd, stdout=pipe_output, stderr=subprocess.STDOUT) while p.poll() is None: # p.poll() returns None while the program is still running # sleep for 1 second time.sleep(1) last_line = open(file_name).readlines() # it''s possible that it hasn''t output yet, so continue if len(last_line) == 0: continue last_line = last_line[-1] # Matching to "[bytes downloaded] number% [speed] number:number:number" match_it = re.match(".* ([0-9]*)%.* ([0-9]*:[0-9]*:[0-9]*).*", last_line) if not match_it: continue # in this case, the percentage is stored in match_it.group(1), # time in match_it.group(2). We could do something with it here...


No se puede hacer que Stdout imprima sin búfer en una tubería (a menos que pueda reescribir el programa que imprime en stdout), así que aquí está mi solución:

Redirigir stdout a sterr, que no está almacenado en el búfer. ''<cmd> 1>&2'' debería hacerlo. Abra el proceso de la siguiente manera: myproc = subprocess.Popen(''<cmd> 1>&2'', stderr=subprocess.PIPE)
No puede distinguir de stdout o stderr, pero obtiene todos los resultados inmediatamente.

Espero que esto ayude a cualquiera a abordar este problema.


Para evitar el almacenamiento en caché de resultados, es posible que desee probar pexpect,

child = pexpect.spawn(launchcmd,args,timeout=None) while True: try: child.expect(''/n'') print(child.before) except pexpect.EOF: break

PD : Sé que esta pregunta es bastante antigua, y aún proporciona la solución que funcionó para mí.

PPS : obtuve esta respuesta de otra pregunta


Sé que este es un tema viejo, pero ahora hay una solución. Llame a rsync con la opción --outbuf = L. Ejemplo:

cmd=[''rsync'', ''-arzv'',''--backup'',''--outbuf=L'',''source/'',''dest''] p = subprocess.Popen(cmd, stdout=subprocess.PIPE) for line in iter(p.stdout.readline, b''''): print ''>>> {}''.format(line.rstrip())


Tu problema es:

for line in p.stdout: print(">>> " + str(line.rstrip())) p.stdout.flush()

el iterador mismo tiene memoria intermedia adicional.

Intenta hacer así:

while True: line = p.stdout.readline() if not line: break print line


Uso | tee | tee para redirigir la stdout a un archivo llamado out.txt mientras se muestra stdout en tiempo real en la terminal

import subprocess, time, os, sys cmd = "rsync.exe -vaz -P source/ dest/ | tee out.txt" p, line = True, ''start'' p = subprocess.Popen(cmd, shell=True) p.wait()

Puede obtener el stdout del archivo out.txt después del subproceso.

# Get stdout from file out.txt f = open(''out.txt'') out = f.read() f.close()


p = subprocess.Popen(command, bufsize=0, universal_newlines=True)

Estoy escribiendo una GUI para rsync en python, y tengo los mismos probelmas. Este problema me ha preocupado por varios días hasta que encuentre esto en pyDoc.

Si universal_newlines es True, los objetos de archivo stdout y stderr se abren como archivos de texto en modo de salto de línea universal. Las líneas pueden terminar con cualquiera de ''/ n'', la convención de fin de línea de Unix, ''/ r'', la antigua convención de Macintosh o ''/ r / n'', la convención de Windows. Todas estas representaciones externas son vistas como ''/ n'' por el programa Python.

Parece que rsync generará ''/ r'' cuando la traducción esté en curso.


for line in p.stdout: ...

siempre bloquea hasta la siguiente alimentación de línea.

Para un comportamiento "en tiempo real" tienes que hacer algo como esto:

while True: inchar = p.stdout.read(1) if inchar: #neither empty string nor None print(str(inchar), end='''') #or end=None to flush immediately else: print('''') #flush for implicit line-buffering break

El while-loop se deja cuando el proceso hijo cierra su stdout o sale. read()/read(-1) se bloquearía hasta que el proceso secundario cerrara su stdout o saliera.