2010-03-09 11 views
10

Tengo un programa interactivo llamado my_own_exe. Primero, imprime alive, luego ingresa S\n y luego imprime alive nuevamente. Finalmente ingresa L\n. Hace algunos procesamientos y salidas.¿por qué python.subprocess se bloquea después de proc.communicate()?

Sin embargo, cuando lo llamo desde la siguiente secuencia de comandos de python, el programa pareció colgarse después de imprimir el primer 'vivo'.

¿Alguien aquí puede decirme por qué sucede esto?

// después de leer las continuaciones (gracias chicos), he modificado el código de la siguiente manera:

import subprocess 
import time 

base_command = "./AO_FelixStrategy_UnitTest --bats 31441 --chix 12467 --enxutp 31884 --turq 26372 --symbol SOGN --target_date " + '2009-Oct-16' 
print base_command 

proc2 = subprocess.Popen(base_command, shell=True , stdin=subprocess.PIPE,) 

time.sleep(2); 
print "aliv" 
proc2.communicate('S\n') 

print "alive" 
time.sleep(6) 

print "alive" 
print proc2.communicate('L\n') 
time.sleep(6) 

el programa ahora va bien con la primera entrada 'S \ n', pero luego se detuvo, y yo, el segundo 'L \ n' es un poco ignorado.

¿Alguien me puede dar una idea de por qué es así?

Respuesta

23

Desde el docs for communicate:

Interactuar con proceso: enviar datos a la entrada estándar. Lea los datos de stdout y stderr, hasta llegar al final del archivo. Espere a que finalice el proceso.

Así que después de communicate() carreras, el proceso ha sido terminado .

Si desea escribir y leer sin esperar a que el proceso para detener:

  • ¿No vez uso shell=True - needlessy se invoca una concha a su vez llame a su programa, por lo que habrá ser otro proceso entre usted y su programa. Eso tiene muchos efectos secundarios desagradables. El valor predeterminado es shell=False, por lo que debe seguir con eso. cambiar su línea Popen a:

    p = subprocess.Popen(["./AO_FelixStrategy_UnitTest", 
             "--bats", "31441", "--chix", "12467", 
             "--enxutp", "31884", "--turq", "26372", 
             "--symbol", "SOGN", "--target_date", '2009-Oct-16'], 
            stdin=subprocess.PIPE, 
            stdout=subprocess.PIPE) 
    
  • Uso p.stdin.write escribir en el proceso. Use p.stdout.read para leer de él.

  • Llamando p.stdout.read si no hay nada que leer bloqueará. Llamar al p.stdin.write si el búfer de escritura está lleno se bloqueará. Por lo tanto, debe asegurarse de tener algo que leer/escribir: lo hace en Unix OS usando select. En Windows desafortunadamente debe recurrir a hilos. Al menos eso es lo que Popen.communicate hace internamente.
  • Si usted no escribió AO_FelixStrategy_UnitTest entonces tienen posibles problemas adicionales:
    • Podría estar leyendo de otra parte, no la entrada estándar. Algunos programas leen directamente desde el terminal, otros usan alguna API del sistema operativo para leer. Eso significa que los datos escritos en stdin no irán al programa. Esto es a menudo cierto para las solicitudes de contraseña.
    • Recuerda que tienes que dar cuenta de los buffers AO_FelixStrategy_UnitTest.Por defecto de comunicación estándar C TUBO es amortiguada por lo que no puede ver ninguna salida hasta después de que haya cerrado el lado de entrada (haciendo p.stdin.close(). A menos que AO_FelixStrategy_UnitTest Vacía la salida periódicamente.

Aquí hay un código de ejemplo, . basa en lo que usted describe podría funcionar dependiendo de cómo se desarrolló AO_FelixStrategy_UnitTest:

p = subprocess.Popen(["./AO_FelixStrategy_UnitTest", 
         "--bats", "31441", "--chix", "12467", 
         "--enxutp", "31884", "--turq", "26372", 
         "--symbol", "SOGN", "--target_date", '2009-Oct-16'], 
        stdin=subprocess.PIPE, 
        stdout=subprocess.PIPE) 
output = p.communicate('S\nL\n')[0] 
print output 
+0

¿No se reinicia el proceso cada vez que llama a 'proc2'? ¿O no lo está llamando ya que tiene impresión delante de dos de ellos? Y si ese es el caso, ¿por qué está congelando después de la primera impresión y no la segunda? – Anthony

+0

@Anthony: No. El proceso no se reinicia. Se está congelando después de la primera impresión porque 'communication' está esperando que el proceso termine, pero el proceso nunca termina porque probablemente esté bloqueado en la segunda solicitud (la que debe ingresar' 'L \ n''). – nosklo

+0

gracias. de todos modos, esto solo resuelve el problema en parte, puedo usar comunicar solo una vez, ¿correcto? si necesito leer y escribir INTERACTIVAMENTE y leer y escribir, este "output = p.communicate ('S \ nL \ n') [0]" no podría funcionar, ¿correcto? –

3

communicate() lee datos de stdout y stderr hasta que se llega al final de su archivo - espera. hasta que tu programa se cierre.

+0

Entonces, ¿cuál es el método correcto para una interacción de ida y vuelta con un subproceso? – Anthony

+0

@ Anthony: La forma correcta es usar 'p.stdin.write()' y 'p.stdout.read()'. Sin embargo, esos pueden bloquear, por lo que usas 'select()' en Unix o hilos en las ventanas para evitar el bloqueo . Comprueba qué '.communicate()' hace al inspeccionar tu código fuente 'subprocess.py'. Comprueba mi respuesta para más detalles. – nosklo

+0

No era mi pregunta, solo intentaba generar algo que el SO pueda encontrar útil para arreglar su código. Eso y yo estábamos realmente curiosos, ya que estaba trabajando en algo vagamente similar en PHP el pasado fin de semana y me gusta saber cómo vive la mejor mitad. – Anthony

0

comunicate sólo se ejecutará una vez y luego se cerrará la tubería, por lo tanto, si usted desea enviar varios comandos para que usted necesita para enviar una después de la otra en la misma cadena .

Aquí es un ejemplo que trabajó para mí después de algunas investigaciones, tratando temas, subprocess32, stdin.write, stdout.read, etc, etc Esta información no está en la información oficial de referencia de Python para comunicarse: https://docs.python.org/2/library/subprocess.html

El el único lugar donde encontré esto fue aquí: Python subprocess communicate kills my process

En cualquier caso, aquí está el código, simple, sin hilos, sin subproceso32, funciona en Linux y Windows. Sí, debe saber cuántas veces envía comandos al otro proceso, pero en general lo sabe. Además de esto se puede añadir hilos, cwd, shell = Verdadero o cualquier otra cosa que desee, pero este es el caso más simple:

def do_commands(self, cmd, parms): 
    proc = subprocess.Popen(cmd, stdout=subprocess.PIPE, stdin=subprocess.PIPE, stderr=subprocess.PIPE) 

    # wait for the process to terminate 
    out, err = process.communicate(cmd_parms) 
    errcode = process.returncode 

    return errcode, out, err 

Así, por ejemplo, si desea enviar varios retornos de carro (\ n) a la aplicación que se llama y un parámetro en el medio (de forma interactiva) lo llamaría algo así:

cmd_parms = "\n\n\n\n\nparm\n\n" 

errcode, out, err = do_commands(command, cmd_parms) 
Cuestiones relacionadas