python子流程中的多个输入和输出进行通信

python子流程中的多个输入和输出进行通信,python,subprocess,stdout,stdin,pexpect,Python,Subprocess,Stdout,Stdin,Pexpect,我需要做一些类似的事情,但我需要创建一个子流程,可以多次提供输入和输出。那篇文章的公认答案有很好的代码 from subprocess import Popen, PIPE, STDOUT p = Popen(['grep', 'f'], stdout=PIPE, stdin=PIPE, stderr=STDOUT) grep_stdout = p.communicate(input=b'one\ntwo\nthree\nfour\nfive\nsix\n')[0] print(gre

我需要做一些类似的事情,但我需要创建一个子流程,可以多次提供输入和输出。那篇文章的公认答案有很好的代码

from subprocess import Popen, PIPE, STDOUT

p = Popen(['grep', 'f'], stdout=PIPE, stdin=PIPE, stderr=STDOUT)    
grep_stdout = p.communicate(input=b'one\ntwo\nthree\nfour\nfive\nsix\n')[0]
print(grep_stdout.decode())

# four
# five
…我想继续这样做:

grep_stdout2 = p.communicate(input=b'spam\neggs\nfrench fries\nbacon\nspam\nspam\n')[0]
print(grep_stdout2.decode())

# french fries
但遗憾的是,我得到了以下错误:

Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/opt/local/Library/Frameworks/Python.framework/Versions/3.4/lib/python3.4/subprocess.py", line 928, in communicate
    raise ValueError("Cannot send input after starting communication")
ValueError: Cannot send input after starting communication
我希望我的脚本能够一次得到一种形式的分析。我试过这样的代码,但不起作用

import pexpect

analyzer = pexpect.spawnu('hfst-lookup analyser-gt-desc.hfstol')
for newWord in ['слово','сработай'] :
    print('Trying', newWord, '...')
    analyzer.expect('> ')
    analyzer.sendline( newWord )
    print(analyzer.before)

# trying слово ...
# 
# trying сработай ...
# слово
# слово слово+N+Neu+Inan+Sg+Acc 0.000000
# слово слово+N+Neu+Inan+Sg+Nom 0.000000
# 
# 

我显然误解了
peexpect.之前所做的事情。如何获得每个单词的输出,一次一个?

无论何时您想要向进程发送输入,请使用
proc.stdin.write()
。每当您想要从进程中获取输出时,请使用
proc.stdout.read()
。构造函数的
stdin
stdout
参数都需要设置为
PIPE
Popen.communicate()
是一种辅助方法,它向
stdin
一次性写入数据,并创建线程从
stdout
stderr
提取数据。当完成数据写入并读取
stdout
stderr
时,它关闭
stdin
,直到这些管道关闭。您无法进行第二次
通信
,因为孩子返回时已经退出

带有子进程的交互式会话要复杂得多

一个问题是子进程是否认识到它应该是交互式的。在大多数命令行程序用于交互的C库中,从终端(例如,linux控制台或“pty”伪终端)运行的程序是交互的,并经常刷新其输出,但从其他程序通过管道运行的程序是非交互的,很少刷新其输出

另一个问题是如何在没有死锁的情况下读取和处理
stdout
stderr
。例如,如果您阻止读取
stdout
,但
stderr
填充了它的管道,那么子系统将停止,您将被卡住。您可以使用线程将两者都拉入内部缓冲区

还有一个问题是你如何处理一个意外离开的孩子


对于像linux和OSX这样的“unixy”系统,编写
pexpect
模块是为了处理交互子进程的复杂性。对于Windows,据我所知,没有好的工具可以做到这一点。

这个答案应该归功于@J.F.Sebastian。谢谢你的评论

以下代码获得了我的预期行为:

import pexpect

analyzer = pexpect.spawn('hfst-lookup analyser-gt-desc.hfstol', encoding='utf-8')
analyzer.expect('> ')

for word in ['слово', 'сработай']:
    print('Trying', word, '...')
    analyzer.sendline(word)
    analyzer.expect('> ')
    print(analyzer.before)
HFST具有Python绑定:

使用这些应该可以避免整个刷新问题,并将为您提供一个比解析pexpect的字符串输出更干净的API

从Python REPL中,您可以使用

dir(hfst)
help(hfst.HfstTransducer)
或者读

抓取文件的相关部分:

istr = hfst.HfstInputStream('hfst-lookup analyser-gt-desc.hfstol')
transducers = []
while not (istr.is_eof()):
    transducers.append(istr.read())
istr.close()
print("Read %i transducers in total." % len(transducers))
if len(transducers) == 1:
  out = transducers[0].lookup_optimize("слово")
  print("got %s" % (out,))
else: 
  pass # or handle >1 fst in the file, though I'm guessing you don't use that feature

“proc.stdin.write()方法不允许您收集输出,”您仍然可以获得输出,您只需要从proc.stdout和proc.stderr获得它。这是windows还是linux?在linux上,pexpect模块是子流程交互的良好选择。您想做什么?如果需要“多个输入和输出”,则必须阅读:顺序应为:0。等待第一个提示1。发送单词2。等待提示,获取单词3的响应(
.after
?)。重复1-2即使只使用标准输入/标准输出,也可能出现死锁。例如,在您写了一些东西之后,您如何知道何时读取grep的标准输出?此外,子进程可能会完全绕过stdin/stdout(典型示例:密码提示)。看见在许多情况下,这些问题可以使用threads、fcntl、async.io:select/poll/epoll/kqueue/iocp和/或pty来解决。和@J.F.Sebastian-还有一些程序可以读取终端类型以进行着色或全屏输出。这可能是一个挑战。如果程序不提供覆盖此类行为的选项(例如
--color
);它可以被认为是一个bug。默认行为应适用于交互式用户–输入更少,输出简洁。除非绝对必要,否则不应该使用全屏。它工作得非常好。proc接收一个输入并发送一个输出。这应小心使用分线器和冲洗管道,因为它可能会造成死锁。在这篇伟大的博文中可以看到更多内容:
AttributeError:module'pexpect'没有属性'spawnu'
@mooncarter它看起来像
pexpect。spawnu
被弃用,而使用
spawn(encoding='utf-8')
。我相应地更新了答案。但是,它仍然在源代码()中,因此我想知道您是否正确安装了
pexpect
istr = hfst.HfstInputStream('hfst-lookup analyser-gt-desc.hfstol')
transducers = []
while not (istr.is_eof()):
    transducers.append(istr.read())
istr.close()
print("Read %i transducers in total." % len(transducers))
if len(transducers) == 1:
  out = transducers[0].lookup_optimize("слово")
  print("got %s" % (out,))
else: 
  pass # or handle >1 fst in the file, though I'm guessing you don't use that feature