2010-07-06 12 views
16

Estoy escribiendo un script python que usa subprocess.Popen para ejecutar dos programas (a partir del código C compilado) que producen stdout. El script obtiene esa salida y la guarda en un archivo. Debido a que el resultado a veces es lo suficientemente grande como para abrumar el subproceso.PIPE, haciendo que el script se cuelgue, envío el stdout directamente al archivo de registro. Quiero que mi script escriba algo al principio y al final del archivo, y entre los dos subprocesos. Llamadas de acceso público. Sin embargo, cuando miro mi archivo de registro, todo lo que escribí en el archivo de registro del script está todo junto en la parte superior del archivo, seguido de todo el stdout ejecutable. ¿Cómo puedo intercalar mi texto agregado al archivo?Guardar stdout del subproceso.Pobre al archivo, además de escribir más cosas en el archivo

def run(cmd, logfile): 
    p = subprocess.Popen(cmd, shell=True, universal_newlines=True, stdout=logfile) 
    return p 

def runTest(path, flags, name): 
    log = open(name, "w") 
    print >> log, "Calling executable A" 
    a_ret = run(path + "executable_a_name" + flags, log) 
    print >> log, "Calling executable B" 
    b_ret = run(path + "executable_b_name" + flags, log) 
    print >> log, "More stuff" 
    log.close() 

El archivo de registro tiene: Calling ejecutable Un Calling ejecutable B Más cosas [... stdout de ambos ejecutables ...]

¿Hay alguna manera de eliminar la salida estándar de la A a la el registro después de llamar a Popen, por ejemplo? Una cosa más que podría ser relevante: el Ejecutable A se inicia y luego se pone en B, y luego de que B imprima y termine, A imprime más y finaliza.

Estoy usando Python 2.4 en RHE Linux.

+0

Cuando usé stdout = subprocess.PIPE y tuve el bucle externo, escribí todo en el archivo de registro, pude intercalar mi propio texto con el resultado de los ejecutables. Cuando no agrego texto, el registro tiene contenido en este orden: 1) Una salida 2) Salida B 3) Resto de una salida. Podría agregar texto antes o después de cada uno de esos pasos. Ahora solo puedo agregar texto al inicio o al final del registro. Agregar un wait() después de que Popen cuelgue el script porque B no se iniciará hasta que A finalice, lo que no ocurrirá, porque A espera el handshaking desde B. ¿Es posible intercalar mi propio texto en el registro con este enfoque? – jasper77

Respuesta

15

Puede llamar a .wait() en cada objeto Popen para asegurarse de que haya finalizado y luego llamar a log.flush(). Tal vez algo como esto:

def run(cmd, logfile): 
    p = subprocess.Popen(cmd, shell=True, universal_newlines=True, stdout=logfile) 
    ret_code = p.wait() 
    logfile.flush() 
    return ret_code 

Si necesita interactuar con el objeto Popen en su función externa se podía mover el .wait() llama a allí en su lugar.

+1

Si pongo wait() en la función run(), el ejecutable B no comenzará a ejecutarse hasta que A finalice, y como A no termina hasta que B lo haga, el script se bloqueará. Sin embargo, descubrí que si ejecuto TestTest(), la función externa, ejecuto A y luego B, luego espero en A y descargo el registro, una línea que imprimo al final de la ejecución de prueba realmente aparece al final del archivo de registro. Todavía no he encontrado una forma de imprimir texto en el archivo justo antes de que B se ejecute. No sé que hay una manera. – jasper77

+2

'logfile.flush()' no tiene ningún efecto en los procesos secundarios. – jfs

1

Debe esperar hasta que el proceso haya terminado antes de continuar. También he convertido el código para usar un administrador de contexto, que es más limpio.

def run(cmd, logfile): 
    p = subprocess.Popen(cmd, shell=True, universal_newlines=True, stdout=logfile) 
    p.wait() 
    return p 

def runTest(path, flags, name): 
    with open(name, "w") as log: 
     print >> log, "Calling executable A" 
     a_ret = run(path + "executable_a_name" + flags, log) 
     print >> log, "Calling executable B" 
     b_ret = run(path + "executable_b_name" + flags, log) 
     print >> log, "More stuff" 
+0

Los administradores de contexto son una característica de python2.6 que no está disponible para cualquiera que todavía esté ejecutando sistemas RHEL5. Hasta que RHEL6 salga, lo mejor es no usarlos. – Jerub

+0

Puede usar gestores de contexto en Python 2.5 usando 'from __future__ import with_statement' antes de cualquier otra importación. – detly

+0

Pero parece que RHEL5 está atascado en Python 2.4. –

2

Digo, simplemente mantenlo real simple. pseudocódigo lógica básica:

write your start messages to logA 
execute A with output to logA 
write your in-between messages to logB 
execute B with output to logB 
write your final messages to logB 
when A & B finish, write content of logB to the end of logA 
delete logB 
+0

Gracias por la sugerencia de pensar fuera de la caja para usar dos archivos de registro separados para A y B en lugar de un solo archivo de registro. Tendré que pensar en eso. – jasper77

1

Según entiendo A espera del programa para B a hacer su cosa y A salidas sólo después de B salidas.

Si B puede empezar sin A correr entonces se podría iniciar los procesos en el orden inverso:

from os.path import join as pjoin 
from subprocess import Popen 

def run_async(cmd, logfile): 
    print >>log, "calling", cmd 
    p = Popen(cmd, stdout=logfile) 
    print >>log, "started", cmd 
    return p 

def runTest(path, flags, name): 
    log = open(name, "w", 1) # line-buffered 
    print >>log, 'calling both processes' 
    pb = run_async([pjoin(path, "executable_b_name")] + flags.split(), log) 
    pa = run_async([pjoin(path, "executable_a_name")] + flags.split(), log) 
    print >>log, 'started both processes' 
    pb.wait() 
    print >>log, 'process B ended' 
    pa.wait() 
    print >>log, 'process A ended' 
    log.close() 

Nota: llamar log.flush() en los procesos principales no tiene efecto en los archivos de memoria intermedia en los procesos hijos.

Si los procesos secundarios utilizan bloques de almacenamiento temporal de la salida estándar entonces se podría tratar de obligarlos a eliminar cuanto antes el uso de pexpect, pty, or stdbuf (se supone que los procesos utilizan la línea de búfer si se ejecuta de forma interactiva o uso de la biblioteca C stdio para E/S).

Cuestiones relacionadas