stderr
,stdout
并stdin
受到特殊对待。当Popen
启动一个新进程时,它必须为这些进程创建与子进程通信的管道,因此指定一个额外的管道与子进程通信并不是那么简单。
如果您需要另一个管道,则需要在执行子流程之前对其进行设置。
这是一个简单的示例,向您展示如何做到这一点。它执行一个测试脚本,该脚本只是将数据从作为命令行参数(或标准输入)给出的文件描述符复制到标准错误(或标准输出):
测试.sh:
#!/bin/bash
read_fd="$1" # get file descriptor from command line
cat # write stdin to stdout
cat <&"$read_fd" >&2 # read from read_fd and write to stderr
调用程序:ptest.py:
import os
import subprocess
pipe_rfd, pipe_wfd = os.pipe()
print(pipe_rfd, pipe_wfd)
p = subprocess.Popen(
["./test.sh", str(pipe_rfd)],
stdin=subprocess.PIPE,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
close_fds=False, # make sure file descriptors are kept open in subprocess
preexec_fn = lambda: os.close(pipe_wfd) # make sure write end is closed in child
)
p.stdin.write(b'spam\n')
p.stdin.close()
os.write(pipe_wfd, b'eggs\n')
os.close(pipe_wfd)
print('out:', p.stdout.read())
print('err:', p.stderr.read())
在 python2 中,您需要close_fds=False
在preexec_fn
生成子节点之前关闭管道的写入端,否则如果写入端在父级中关闭,则读取端将看不到 EOF。从 python3.2 开始,您可以改为使用新pass_fds
参数来提供要保持打开的文件描述符列表,但上面的代码也可以工作(仅在 linux 上测试过)。
应用于您的问题,Popen
调用将如下所示:
...
with Popen(
[
'ffmpeg',
'-f', 'matroska', '-i', 'pipe:0',
'-f', 'matroska', '-i', 'pipe:%d' % pipe_rfd,
],
stdin=PIPE, pass_fds=[pipe_rfd]) as p:
...