繁体   English   中英

如何作为连续会话与python的子进程进行交互

[英]How to interact with python's subprocess as a continuous session

我需要在python中实现一个过滤器,该过滤器会从Linux命令行字典工具中选取特定的输出。 我需要:

  1. 从文件中获取一组单词
  2. 查找每个单词:1)如果单词不包含,请跳过它; 2)否则,如果是动词,请保存定义。

为了测试代码,我写了两个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.pyfor循环中读取。 是什么原因?

由于需求,我需要在每次输入新命令时获取全部输出。 好像是对话会话。 所以subprocess.communicate()在那里毫无用处,因为它总是终止当前的子进程。 如何实现这一需求?

subprocess坚持使用.communicate()的基本原因是,否则可能会发生死锁。 假设您正在写入进程的标准输入,而进程正在写入其标准输出。 如果管道缓冲区已满,则写入将阻塞,直到发生读取为止。 然后你们都在互相等待,无法取得任何进展。 有几种方法可以解决此问题:

  1. 使用单独的线程。 将一个分配给stdin,将另一个分配给stdout。 这样,如果一个管道阻塞,您仍在维修另一个。
  2. 使用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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM