我需要做this post之类的事情,但是我需要创建一个可以给出输入并多次输出的子进程。该帖子的公认答案有很好的代码......
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
如果我理解正确的话,proc.stdin.write()方法不能让你收集输出。保持线路开放以进行持续输入/输出的最简单方法是什么?
编辑:====================
看起来pexpect
对于我想要做的事情来说是一个有用的库,但是我无法让它工作。这是对我实际任务的更完整的解释。我正在使用hfst
来获得个别(俄语)单词的语法分析。以下演示了它在bash shell中的行为:
$ hfst-lookup analyser-gt-desc.hfstol
> слово
слово слово+N+Neu+Inan+Sg+Acc 0.000000
слово слово+N+Neu+Inan+Sg+Nom 0.000000
> сработай
сработай сработать+V+Perf+IV+Imp+Sg2 0.000000
сработай сработать+V+Perf+TV+Imp+Sg2 0.000000
>
我希望我的脚本能够一次获得一个表单的分析。我试过像这样的代码,但它没有用。
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
#
#
我显然误解了pexpect.before
的作用。如何获得每个单词的输出,一次一个?
答案 0 :(得分:18)
Popen.communicate()
是一种辅助方法,可以将数据一次性写入stdin
并创建线程以从stdout
和stderr
提取数据。它完成写入数据后会关闭stdin
并读取stdout
和stderr
,直到这些管道关闭。您不能再做一秒communicate
,因为孩子在返回时已经退出。
与子进程的交互式会话相当复杂。
一个问题是子进程是否认识到它应该是交互式的。在大多数命令行程序用于交互的C库中,从终端(例如,linux控制台或“pty”伪终端)运行的程序是交互式的并且经常刷新它们的输出,但是通过PIPES从其他程序运行的程序是非互动并不经常刷新他们的输出。
另一个是如何阅读和处理stdout
和stderr
而不会发生死锁。例如,如果您阻止阅读stdout
,但stderr
填充其管道,则孩子将停止并且您被卡住了。您可以使用线程将两者都拉入内部缓冲区。
另一个是你如何处理一个意外退出的孩子。
对于像linux和OSX这样的“unixy”系统,编写pexpect
模块来处理交互式子进程的复杂性。对于Windows,我知道没有好的工具可以做到。
答案 1 :(得分:11)
这个答案应该归功于@ J.F.Sebastian。感谢您的评论!
以下代码得到了我预期的行为:
import pexpect
analyzer = pexpect.spawnu('hfst-lookup analyser-gt-desc.hfstol')
analyzer.expect('> ')
for word in ['слово', 'сработай']:
print('Trying', word, '...')
analyzer.sendline(word)
analyzer.expect('> ')
print(analyzer.before)
答案 2 :(得分:3)
每当您想要向流程发送输入时,请使用proc.stdin.write()
。只要您想从流程中获取输出,请使用proc.stdout.read()
。构造函数的stdin
和stdout
参数都需要设置为PIPE
。
答案 3 :(得分:2)
HFST具有Python绑定:https://pypi.python.org/pypi/hfst
使用它们应该避免整个刷新问题,并且将为您提供一个更清晰的API,而不是解析pexpect的字符串输出。
从Python REPL中,你可以通过
获得一些关于绑定的文档dir(hfst)
help(hfst.HfstTransducer)
或阅读https://hfst.github.io/python/3.12.2/QuickStart.html
抢夺文档的相关部分:
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