python子进程中的多个输入和输出通信
Posted
技术标签:
【中文标题】python子进程中的多个输入和输出通信【英文标题】:Multiple inputs and outputs in python subprocess communicate 【发布时间】:2015-04-21 08:48:20 【问题描述】:我需要做类似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
的作用。如何获得每个单词的输出,一次一个?
【问题讨论】:
"proc.stdin.write() 方法不能让你收集输出," 你仍然可以得到输出,你只需要从 proc.stdout 和 proc.stderr 中得到它。 这是windows还是linux?在 linux 上,pexpect 模块是子进程交互的不错选择。 你想做什么? 如果你想要“多输入多输出”必读:Q: Why not just use a pipe (popen())? 顺序应该是: 0. 等待第一个提示 1. 发送单词 2. 等待提示,得到单词的响应(.after
?) 3. 重复1-2跨度>
【参考方案1】:
Popen.communicate()
是一种辅助方法,它一次性将数据写入stdin
并创建线程以从stdout
和stderr
中提取数据。它在完成写入数据后关闭 stdin
并读取 stdout
和 stderr
直到这些管道关闭。你不能再做第二个communicate
,因为孩子在它返回时已经退出了。
与子进程的交互式会话要复杂得多。
一个问题是子进程是否甚至认识到它应该是交互式的。在大多数命令行程序用于交互的 C 库中,从终端(例如,linux 控制台或“pty”伪终端)运行的程序是交互的并经常刷新它们的输出,但通过 PIPES 从其他程序运行的程序是非不经常交互并刷新它们的输出。
另一个是您应该如何阅读和处理stdout
和stderr
而不会出现死锁。例如,如果您阻止读取stdout
,但stderr
填满了它的管道,那么孩子将停止并且您被卡住。您可以使用线程将两者都拉入内部缓冲区。
还有一个问题是你如何处理意外退出的孩子。
对于像 linux 和 OSX 这样的“unixy”系统,pexpect
模块用于处理交互式子进程的复杂性。对于 Windows,没有我知道的好工具可以做到这一点。
【讨论】:
即使只使用标准输入/标准输出也可能会死锁,例如,你怎么知道在你写完东西后什么时候从 grep 的标准输出中读取?此外,子进程可能会完全绕过标准输入/标准输出(典型示例:密码提示)。见the link in the comment above。在许多情况下,这些问题可以使用线程、fcntl、async.io 来解决:select/poll/epoll/kqueue/iocp 和/或 pty。还有sometimes it is enough to be careful @J.F.Sebastian - 然后有程序读取终端类型以着色或全屏输出。这可能是一个挑战。 如果程序没有提供覆盖这种行为的选项(例如--color
);它可以被认为是一个错误。默认行为应该是针对交互式用户的——更少的输入,简洁的输出。除非绝对必要,否则不应使用全屏。【参考方案2】:
这个答案应该归功于@J.F.Sebastian。感谢 cmets!
以下代码得到了我的预期行为:
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)
【讨论】:
AttributeError: module 'pexpect' has no attribute 'spawnu'
@Mooncrater 看起来 pexpect.spawnu
已被弃用,取而代之的是使用 spawn(encoding='utf-8')
。我相应地更新了答案。但是,它还在源代码中(github.com/pexpect/pexpect/blob/master/pexpect/…),所以不知道您是否正确安装了pexpect
。
期望模拟与终端的交互是否良好,因为程序在管道中会改变它们的行为。但是,如果使用二进制数据进行通信,那就不好了。
@Camion 用于二进制数据,只需省略 encoding='utf-8'
参数。默认情况下,spawn
需要二进制。
我认为没有那么简单,因为新的行处理。我现在没有检查它,但我相信即使没有编码 pexpect 也会将行尾字符转换为像行尾(\r\n
)这样的窗口,如果你的数据结束,你也可能会遇到问题不是换行。【参考方案3】:
只要您想向进程发送输入,请使用proc.stdin.write()
。每当您想从流程中获取输出时,请使用proc.stdout.read()
。构造函数的stdin
和stdout
参数都需要设置为PIPE
。
【讨论】:
完美运行。 proc 接受一个输入并发出一个输出。 这应该谨慎使用分行器和刷新 PIPE,因为它可能会造成死锁。在这篇精彩的博文中可以了解更多信息:eli.thegreenplace.net/2017/…【参考方案4】:HFST 有 Python 绑定:https://pypi.python.org/pypi/hfst
使用这些应该可以避免整个刷新问题,并且会为您提供比解析 pexpect 的字符串输出更简洁的 API。
从 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
【讨论】:
以上是关于python子进程中的多个输入和输出通信的主要内容,如果未能解决你的问题,请参考以下文章
从Python中的imagemagick子进程读取输出[重复]
Python asyncio子进程连续写入stdin和读取stdout/stderr