Sto provando a scrivere un programma python in grado di interagire con altri programmi. Ciò significa inviare stdin e ricevere dati stdout. Non riesco a usare pexpect (sebbene abbia sicuramente ispirato parte del design). Il processo che sto utilizzando in questo momento è questa:Utilizzo del sottoprocesso con select e pty si blocca quando si cattura l'output
- Collegare un pty sullo standard output del sottoprocesso
- loop fino a quando le uscite di processo parziali controllando
subprocess.poll
- Quando ci sono dati disponibili nel stdout scrivere che i dati immediatamente allo stdout corrente.
- Fine!
Sto prototipando un codice (di seguito) che funziona ma sembra avere un difetto che mi infastidisce. Al completamento del processo secondario, il processo padre si blocca se non si specifica un timeout quando si utilizza select.select
. Preferirei davvero non impostare un timeout. Sembra solo un po 'sporco. Tuttavia, tutti gli altri modi in cui ho cercato di aggirare il problema non sembrano funzionare. Pexpect sembra aggirarlo usando os.execv
e pty.fork
invece di subprocess.Popen
e pty.openpty
una soluzione che non preferisco. Sto facendo qualcosa di sbagliato con come controllo la vita del sottoprocesso? Il mio approccio è errato?
Il codice che sto utilizzando è di seguito. Lo sto usando su Mac OS X 10.6.8, ma ho bisogno che funzioni anche su Ubuntu 12.04.
Questo è il corridore sottoprocesso runner.py
:
import subprocess
import select
import pty
import os
import sys
def main():
master, slave = pty.openpty()
process = subprocess.Popen(['python', 'outputter.py'],
stdin=subprocess.PIPE,
stdout=slave, stderr=slave, close_fds=True)
while process.poll() is None:
# Just FYI timeout is the last argument to select.select
rlist, wlist, xlist = select.select([master], [], [])
for f in rlist:
output = os.read(f, 1000) # This is used because it doesn't block
sys.stdout.write(output)
sys.stdout.flush()
print "**ALL COMPLETED**"
if __name__ == '__main__':
main()
Questo è il codice sottoprocesso outputter.py
. Le strane parti casuali servono solo a simulare un programma che emette dati a intervalli casuali. Puoi rimuoverlo se lo desideri. Esso non dovrebbe importare:
import time
import sys
import random
def main():
lines = ['hello', 'there', 'what', 'are', 'you', 'doing']
for line in lines:
sys.stdout.write(line + random.choice(['', '\n']))
sys.stdout.flush()
time.sleep(random.choice([1,2,3,4,5])/20.0)
sys.stdout.write("\ndone\n")
sys.stdout.flush()
if __name__ == '__main__':
main()
Grazie per tutto l'aiuto che tutto può fornire!
nota Extra
Pty viene utilizzato perché voglio assicurare che stdout non è tamponata.
Grazie per la fantastica spiegazione. Ho pensato, dopo un po ', che probabilmente sarebbe stato meglio impostare un timeout. Ho provato la soluzione di izhak ma sì, ho visto un comportamento molto strano dopo averlo fatto. Questo aiuta molto! – ravenac95
Per il mio miglioramento, puoi spiegare perché la mia risposta è venuta meno? Dovrebbe farti evitare di usare qualsiasi timeout. –
Ho implementato i tuoi suggerimenti in [la risposta a una domanda correlata] (http://stackoverflow.com/a/12471855/4279) – jfs