[英]How to interact with python's subprocess as a continuous session
我需要在python中实现一个过滤器,该过滤器会从Linux命令行字典工具中选取特定的输出。 我需要:
为了测试代码,我写了两个python文件:
# name.py
import sys
while True:
print 'name => Q: what is your name?'
sys.stdout.flush()
name = raw_input()
if name == 'Exit':
break
print 'name => A: your name is ' + name
sys.stdout.flush()
# test.py
import subprocess
child = subprocess.Popen(r'python name.py',
stdin = subprocess.PIPE,
stdout = subprocess.PIPE,
stderr = subprocess.STDOUT,
shell = True)
commandlist = ['Luke\n', 'Mike\n', 'Jonathan\n', 'Exit\n']
for command in commandlist:
child.stdin.write(command)
child.stdin.flush()
output = child.stdout.readline()
print 'From PIPE: ' + output
while child.poll() is None:
print 'NOT POLL: ' + child.stdout.readline()
child.wait()
输出是
From PIPE: name => Q: what is your name?
From PIPE: name => A: your name is Luke
From PIPE: name => Q: what is your name?
From PIPE: name => A: your name is Mike
# these lines need to start with "From PIPE" ...
NOT POLL: name => Q: what is your name?
NOT POLL: name => A: your name is Jonathan
NOT POLL: name => Q: what is your name?
NOT POLL:
稍后的输出在while
循环中读取,而不是在test.py
的for
循环中读取。 是什么原因?
由于需求,我需要在每次输入新命令时获取全部输出。 好像是对话会话。 所以subprocess.communicate()
在那里毫无用处,因为它总是终止当前的子进程。 如何实现这一需求?
subprocess
坚持使用.communicate()
的基本原因是,否则可能会发生死锁。 假设您正在写入进程的标准输入,而进程正在写入其标准输出。 如果管道缓冲区已满,则写入将阻塞,直到发生读取为止。 然后你们都在互相等待,无法取得任何进展。 有几种方法可以解决此问题:
select
在管道上多路复用。 仅与准备好您的管道交互。 您还应该使用fcntl
在管道上启用O_NONBLOCK
,以免意外填充缓冲区。 正确使用,可以防止管道阻塞,因此不会死锁。 这在Windows下不起作用 ,因为您只能在该处的套接字上进行select
。 在您的特定情况下,问题在于子进程打印的每两行,父进程仅读取一行。 如果您传递更多的名称,则在操作系统管道缓冲区被填满后,最终您的进程将陷入僵局,如@Kevin所述 。
要解决此问题,只需在将名称写入子进程之前添加第二个child.stdout.readline()
来阅读问题。
例如,这是parent.py
脚本:
#!/usr/bin/env python
from __future__ import print_function
import sys
from subprocess import Popen, PIPE
child = Popen([sys.executable, '-u', 'child.py'],
stdin=PIPE, stdout=PIPE,
bufsize=1, universal_newlines=True)
commandlist = ['Luke', 'Mike', 'Jonathan', 'Exit']
for command in commandlist:
print('From PIPE: Q:', child.stdout.readline().rstrip('\n'))
print(command, file=child.stdin)
#XXX you might need it to workaround bugs in `subprocess` on Python 3.3
#### child.stdin.flush()
if command != 'Exit':
print('From PIPE: A:', child.stdout.readline().rstrip('\n'))
child.stdin.close() # no more input
assert not child.stdout.read() # should be empty
child.stdout.close()
child.wait()
From PIPE: Q: name => Q: what is your name?
From PIPE: A: name => A: your name is Luke
From PIPE: Q: name => Q: what is your name?
From PIPE: A: name => A: your name is Mike
From PIPE: Q: name => Q: what is your name?
From PIPE: A: name => A: your name is Jonathan
From PIPE: Q: name => Q: what is your name?
该代码可以工作,但是如果child.py
进程的输出可能更改,则死锁可能再次出现,它仍然很脆弱。 pexpect
模块解决了控制交互过程的许多问题 。 另请参见此注释中链接的代码示例 。
我将child.py
更改为可在Python 2和3上使用:
#!/usr/bin/env python
try:
raw_input = raw_input
except NameError: # Python 3
raw_input = input
while True:
print('name => Q: what is your name?')
name = raw_input()
if name == 'Exit':
break
print('name => A: your name is ' + name)
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.