我需要在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
循环期间读取后面的输出,而不是for
中的test.py
循环。是什么原因?
由于需求,我需要在每次输入新命令时获得整个输出。这似乎是一个对话会话。所以subprocess.communicate()
在那里没用,因为它总是终止当前的子进程。如何实现这一需求?
答案 0 :(得分:3)
subprocess
坚持使用.communicate()
的基本原因是因为否则可能会发生死锁。假设您正在写入进程的stdin,而进程正在写入其stdout。如果管道缓冲区填满,则写入将阻塞,直到发生读取。然后你们都在等着彼此,无法取得进展。有几种方法可以解决这个问题:
select
在管道上进行多路复用。只与您准备好的管道互动。您还应该使用O_NONBLOCK
在管道上启用fcntl
,这样您就不会意外地填充缓冲区。正确使用,这将防止管道阻塞,因此您不能死锁。 这不适用于Windows ,因为您只能在那里的套接字上执行select
。答案 1 :(得分:2)
在您的特定情况下,问题是对于子进程打印的每两行,您的父进程只读取一行。如果传递更多名称,那么在OS管道缓冲区填充为@Kevin explained之后,最终会使进程死锁。
要修复它,只需添加第二个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
进程的输出可能会发生变化,那么它仍然很脆弱,然后可能会再次出现死锁。 Many issues to control an interactive process are solved by pexpect
module。另请参阅the code example linked in this comment。
我已将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)