如何用python的子相互作用的连续对话(How to interact with python&#

2019-10-22 04:35发布

我需要实现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循环,而不是for循环中test.py 。 是什么原因?

由于需求,我每次都需要得到整个输出输入它的新命令。 这似乎是一个对话会议。 所以subprocess.communicate()是没用的存在,因为它总是终止当前子进程。 如何实现这个需求呢?

Answer 1:

其根本原因subprocess坚持使用.communicate()是因为否则发生死锁是可能的。 假设你写进程的标准输入,而过程写入其标准输出。 如果管道缓冲区填满,写操作将阻塞,直到读发生。 然后,你们都在等待对方,没有能够取得进展。 有几种方法来解决这个问题:

  1. 使用单独的线程。 指定一个标准输入,另一个到标准输出。 这样,如果一个管块,你还在维修等。
  2. 使用select复用在管道。 只有喉管,随时为您服务交互。 你也应该让O_NONBLOCK使用管道fcntl ,所以你不小心填补了缓冲区。 正确使用,这样可以防止管道从不断阻止,所以你不能死锁。 这无法在Windows下工作 ,因为你只能做select上的插座那里。


Answer 2:

在特定情况下,问题是,每个两行子进程版画,你的父进程只读取一行。 如果传递的名字那么最终你的进程死锁操作系统管缓冲区已经被填满后,作为@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)


文章来源: How to interact with python's subprocess as a continuous session