2010-03-09 16 views
10

Ho un programma interattivo chiamato my_own_exe. Innanzitutto, stampa alive, quindi immette S\n e quindi stampa nuovamente alive. Infine, inserisci L\n. Funziona ed esce.perché python.subprocess si blocca dopo proc.communicate()?

Tuttavia, quando lo chiamo dal seguente script python, il programma sembrava bloccarsi dopo aver stampato il primo 'vivo'.

Qualcuno può dirmi perché questo sta accadendo?

// dopo aver letto le follow up (grazie ragazzi), ho modificato il codice come segue:

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) 

il programma ora va bene con il primo ingresso 'S \ n', ma poi si fermò, e io il secondo 'L \ n' è un po 'ignorato.

Qualcuno può darmi un'idea del perché è così?

risposta

23

Dal docs for communicate:

Interagisci con le procedure: inviare dati a stdin. Leggi i dati da stdout e stderr, fino al raggiungimento della fine del file. Attendere il termine del processo.

Così, dopo communicate() corse, il processo è stato terminato .

Se si desidera scrivere e leggere senza attendere il processo di smettere:

  • Non mai uso shell=True - si invoca needlessy un guscio a sua volta chiamare il vostro programma, quindi non ci sarà essere un altro processo tra te e il tuo programma. Questo ha molti spiacevoli effetti collaterali. Il valore predefinito è shell=False quindi è necessario attenersi a questo. Cambia la tua linea di 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) 
    
  • Usa p.stdin.write di scrivere al processo. Utilizzare p.stdout.read per leggerlo.

  • Chiamare p.stdout.read se non c'è niente da leggere bloccherà. Chiamando p.stdin.write se il buffer di scrittura è pieno si bloccherà. Quindi devi assicurarti di avere qualcosa da leggere/scrivere - lo fai su UNIX OS usando select. Su Windows, purtroppo, è necessario ricorrere ai thread. Almeno questo è ciò che internamente fa Popen.communicate.
  • Se non hai scritto AO_FelixStrategy_UnitTest allora avete eventuali ulteriori problemi:
    • Potrebbe essere la lettura da qualche altra parte, non un input standard. Alcuni programmi leggono direttamente dal terminale, altri usano alcune API del sistema operativo per leggere. Ciò significa che i dati scritti su stdin non andranno al programma. Questo è spesso vero per i prompt della password.
    • Ricordare che è necessario tenere conto dei buffer AO_FelixStrategy_UnitTest.Per impostazione predefinita di comunicazione standard C PIPE è tamponato in modo da non vedere alcuna uscita solo dopo aver chiuso il lato di ingresso (facendo p.stdin.close(). A meno che AO_FelixStrategy_UnitTest scarica periodicamente l'uscita.

Ecco qualche esempio di codice, . basa su ciò che si descrive potrebbe funzionare a seconda di come è stato sviluppato 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

Il processo non viene riavviato ogni volta che chiama 'proc2'? O non lo chiama da quando ha stampato di fronte a due di loro? E se questo è il caso, perché è il congelamento dopo la prima stampa e non la seconda? – Anthony

+0

@Anthony: No. Il processo non viene riavviato. Si sta congelando dopo la prima stampa perché 'communicate' sta aspettando che il processo finisca, ma il processo non finisce mai perché è probabilmente bloccato sul secondo prompt (quello in cui' 'L \ n'' deve essere inserito). – nosklo

+0

grazie. comunque, questo risolve solo parzialmente il problema, posso usare comunicare una sola volta, corretto? se ho bisogno di leggere, scrivere, leggere e scrivere INTERATTIVAMENTE, questo "output = p.communicate ('S \ nL \ n') [0]" non funziona, corretto? –

3

communicate() legge i dati da stdout e stderr fino alla fine del file viene raggiunta - aspetta. fino a quando il tuo programma non si chiude.

+0

Quindi qual è il metodo giusto per un'interazione avanti e indietro con un sottoprocesso? – Anthony

+0

@ Anthony: Il modo corretto è usare 'p.stdin.write()' e 'p.stdout.read()'. Tuttavia questi possono bloccarsi, quindi usi 'select()' su unix o thread su windows per evitare il blocco Controlla cosa '.communicate()' fa ispezionando il tuo codice sorgente 'subprocess.py'. Controlla la mia risposta per maggiori dettagli. – nosklo

+0

Non era una mia domanda, stavo solo cercando di generare qualcosa che il SO potesse trovare utile per correggere il suo codice. Questo ed io eravamo sinceramente curiosi, mentre stavo lavorando a qualcosa di vagamente simile in PHP lo scorso fine settimana e mi piace sapere come vivono le vite migliori. – Anthony

0

comunicare verrà eseguito solo una volta e poi chiuderà il tubo, quindi, se si desidera inviare più comandi ad esso è necessario inviare una dopo l'altra nella stessa stringa.

Ecco un esempio che ha funzionato per me, dopo alcune indagini, cercando le discussioni, subprocess32, stdin.write, stdout.read ecc ecc Questa informazione non è nella informazione ufficiale di riferimento di Python per comunicare: https://docs.python.org/2/library/subprocess.html

Il solo il luogo in cui ho trovato questo era qui: Python subprocess communicate kills my process

In ogni caso, qui è il codice, semplice, senza thread, nessun sottoprocesso32, funziona su linux e windows. Sì, devi sapere quante volte inviare comandi all'altro processo, ma in generale lo sai. In cima a questo si può aggiungere le discussioni, CWD, shell = True o qualsiasi altra cosa si potrebbe desiderare, ma questo è il caso più semplice:

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 

Così, per esempio, se si desidera inviare più ritorni a capo (\ n) l'applicazione viene chiamata e il param nel mezzo (in modo interattivo) si chiamerebbe qualcosa del genere:

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

errcode, out, err = do_commands(command, cmd_parms) 
Problemi correlati