我有一个进程,它是这样创建的:
p = subprocess.Popen(args = './myapp',
stdin = subprocess.PIPE,
stdout = subprocess.PIPE,
universal_newlines=True)
稍后,我尝试写入 p
的 stdin
:
p.stdin.write('my message\n')
myapp
进程具有以下设置:
q = queue.Queue()
def get_input():
for line in iter(sys.stdin.readline, ''):
q.put(line)
sys.stdin.close()
threading.Thread(name = 'input-getter',
target = get_input).start()
它正在尝试连续读取新行,如下所示:
try:
print('input:', q.get_nowait())
except Empty:
print('no input')
不幸的是,子进程从未收到我的任何消息。当然,当我使用:
p.communicate('my message\n')
子进程收到消息,但正如预期的那样,communicate
方法关闭了 p
的 stdin
,因此没有更多的通信进行上。
最佳答案
p = subprocess.Popen(args = './myapp',
stdin = subprocess.PIPE,
stdout = subprocess.PIPE,
universal_newlines=True)
while p.poll() is None:
data = p.stdout.readline()
这将创建一个非阻塞的进程读取,直到进程退出。
但是,这里有一些注意事项需要注意。例如,如果您也通过管道传输 stderr
,但不从中读取...那么您很可能会填充一两个缓冲区,并且无论如何都会挂起程序。因此,请始终确保在手动执行操作时清除所有缓冲区 I/O。
如果可能的话,一个更好的选择是使用 select.epoll()
,这只在 unix 系统上可用,但会给你带来更好的性能和错误处理:)
epoll = select.epoll()
epoll.register(p.stdout.fileno(), select.EPOLLHUP) # Use select.EPOLLIN for stdin.
for fileno, event in epoll.poll(1):
if fileno == p.stdout.fileno():
# ... Do something ...
注意:请记住,每当进程需要输入时,它通常会通过 stdout
进行指示,因此您仍然需要使用 STDOUT
注册 select.epoll
以检查“等待输入”。您可以注册 select.EPOLLIN
以检查是否提供了输入,但我几乎不明白这一点,因为请记住,这就是您选择输入到您应该已经知道的过程的内容是“正在发生的”。
检查进程是否需要输入
您可以使用 select.epoll
来检查进程是否正在等待输入,而不会在上面的示例中阻止您的应用程序执行。但还有更好的选择。
Pexpect是一个做得非常好的库,例如与 SSH
一起工作。
它的工作方式与子流程略有不同,但可能是一个不错的选择。
让 subprocess.popen 与 SSH 一起工作
如果这是您想要的,我将重定向到另一个问题+答案(因为 SSH 将以 protected 方式生成 stdin
。
Python + SSH Password auth (no external libraries or public/private keys)?
关于python - 如何从 `stdin` 创建非阻塞连续读取?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/30217916/