如何在Python中使用pexpect获取子进程的自发输出
这段内容和我之前的一篇帖子有关,帖子链接是关于wx.TextCtrl的多线程问题。在我修正了从主线程调用GUI交互后,我发现又遇到了管道阻塞的问题。那么,如何才能从subprocess.stdout中获得即时输出呢?
简单来说,我现在使用subprocess.popen来启动一个外部的长时间运行的程序。
launchcmd=["EXTERNAL_PROGRAM_EXE"]
p = subprocess.Popen(launchcmd, stdin=subprocess.PIPE,
stdout=subprocess.PIPE, stderr=subprocess.PIPE)
self.outputThread = BashProcessThread(p.stdout.readline)
self.outputThread.start()
# wx.TextCtrl is used to make input/output
self.textctrl = wx.TextCtrl(self, style=wx.TE_PROCESS_ENTER|wx.TE_MULTILINE)
我还用一个单独的线程来读取这个后台程序的标准输出,并通过“wx.CallAfter”来进行回调。
class BashProcessThread(threading.Thread):
def __init__(self, readlineFunc, textctrl):
threading.Thread.__init__(self)
self.readlineFunc = readlineFunc
def run(self):
while True:
line = self.readlineFunc()
wx.CallAfter(textctrl.AppendText(line))
上面的代码打印出的子进程日志信息是成块输出的,而不是一行一行地输出,最糟糕的是,剩下的5-6行日志信息直到用户发送下一个输入时才会被及时打印出来。
从我之前的帖子中,我了解到有pty和pexpect这两个工具,它们可以让子进程觉得自己在和一个伪终端(pseudo-tty)交互。但是,pexpect应该怎么用呢?特别是考虑到这个后台进程是一个长期独立运行的任务。
例如,如果我使用了
child=pexpect.spawn(launchcmd)
我该如何获取子进程的输入和输出,这样我就可以用wx.TextCtrl来打印输出,同时也能用wx.TextCtrl将用户输入转发给子进程呢?
2 个回答
3
我发现这两种方法在获取实时输出方面效果很好。
如果你不想让用户进行交互,比如在后台运行的程序:
child = pexpect.spawn(launchcmd)
child.logfile = sys.stdout
child.expect(pexpect.EOF)
child.close()
如果你不是在后台运行程序,并且想要和程序进行互动(比如程序提示你输入内容)。在这种情况下,你会进入交互模式,pexpect会直接把信息写到屏幕上。当程序运行到结束或文件结束(EOF)时,会抛出一个OSError错误。
child = pexpect.spawn(launchcmd)
try:
child.interact()
except OSError:
pass
child.close()
13
你有没有试过类似这样的代码:
child = pexpect.spawn(launchcmd)
while True:
try:
child.expect('\n')
print(child.before)
except pexpect.EOF:
break