Python 3.x 如何将数据写入Python shell管道中第一个进程的stdin?

Python 3.x 如何将数据写入Python shell管道中第一个进程的stdin?,python-3.x,subprocess,posix,pipeline,Python 3.x,Subprocess,Posix,Pipeline,在关于Python子流程管道的讨论中,我看到这个代码片段被大量引用。强制性链接: 稍加修改: p1 = subprocess.Popen(['cat'], stdin=subprocess.PIPE, stdout=subprocess.PIPE) p2 = subprocess.Popen(['head', '-n', '1'], stdin=p1.stdout,

在关于Python子流程管道的讨论中,我看到这个代码片段被大量引用。强制性链接:

稍加修改:

p1 = subprocess.Popen(['cat'],
                      stdin=subprocess.PIPE,
                      stdout=subprocess.PIPE)
p2 = subprocess.Popen(['head', '-n', '1'],
                      stdin=p1.stdout,
                      stdout=subprocess.PIPE)
# Allow p1 to receive a SIGPIPE if p2 exits.
p1.stdout.close()
output = p2.communicate()[0]
这个shell管道是没有意义的,除了简洁地演示挑战。输入
“abc\ndef\nghi\n”
,只有
“abc\n”
应在
输出中捕获

将数据写入
p1.stdin
的最佳方式是什么?我知道
subprocess.Popen.communicate()
input
参数,但它在管道中不起作用。此外,解决方案需要正确处理阻塞


我的猜测是:对
communicate()
背后的代码进行反向工程,并为此特定问题创建另一个版本。在我这样做之前,我想问一下是否有我不知道的更简单的解决方案。

写入
p1.stdin
,然后在调用
p2.communicate()之前关闭它:


(不要忘记发送到
cat
的数据中的换行符,否则它将不起作用。)

您需要同时调用
p1.communicate(b“abc\ndef\nghi\n”)
output=p2.communicate()[0]
。一种可移植的方法是使用线程或
asyncio

作为一个工作示例:

import subprocess, threading

# Unmodified from original code
p1 = subprocess.Popen(['cat'],
                      stdin=subprocess.PIPE,
                      stdout=subprocess.PIPE)
p2 = subprocess.Popen(['head', '-n', '1'],
                      stdin=p1.stdout,
                      stdout=subprocess.PIPE)

# New code: Start a thread that writes stdin, and closes it when complete
def write_stdin():
    p1.stdin.write("abc\ndef\nghi\n")
    p1.stdin.close()

write_t = threading.Thread(target = write_stdin)
write_t.start()

# Unmodified from original code
p1.stdout.close()
output = p2.communicate()[0]

我认为如果数据对于stdin非常大,或者进程阻塞stdin,您的解决方案将阻塞。根据我的经验,这是很常见的。如果子进程阻塞(不处理)stdin,那么您对此几乎无能为力。然后,
write
调用将引发
BlockingError
,这将向您发出管道被阻塞的信号,您应该稍后重试。@kevinarpe:只要第二个子进程在第一个子进程完成读取之前没有开始写入;不应该有任何问题。否则(如您所述)可能会出现死锁。@罗兰·史密斯:如果输入和输出是同时进行的,非阻塞io会有帮助。@J.F.Sebastian但是如何强制非阻塞io呢?我在
io
模块中看不到任何控制它的东西。您可以在使用
open()
时控制缓冲,但由于子进程的管道是为您打开的,因此我看不到强制执行的方法。@RolandSmith:“如何”取决于“您想要什么”,例如,您可以使用
fcntl
为管道设置
O_NONBLOCK
标志,以便
.write()
将引发与
EAGAIN
eWoldBlock
errno相对应的异常。就个人而言,我更喜欢基于线程、asyncio、twisted、gevent的解决方案,如果我需要异步I/O,请选择模块。
import subprocess, threading

# Unmodified from original code
p1 = subprocess.Popen(['cat'],
                      stdin=subprocess.PIPE,
                      stdout=subprocess.PIPE)
p2 = subprocess.Popen(['head', '-n', '1'],
                      stdin=p1.stdout,
                      stdout=subprocess.PIPE)

# New code: Start a thread that writes stdin, and closes it when complete
def write_stdin():
    p1.stdin.write("abc\ndef\nghi\n")
    p1.stdin.close()

write_t = threading.Thread(target = write_stdin)
write_t.start()

# Unmodified from original code
p1.stdout.close()
output = p2.communicate()[0]