3

在 Popen 中,我可以写入标准输入(0)并从标准输出(1)和标准错误(2)读取。

我想做这样的事情:

#!/usr/bin/env python3
from subprocess import Popen, PIPE
with Popen(
        [
            'ffmpeg',
            '-f', 'matroska', '-i', 'pipe:0',
            '-f', 'matroska', '-i', 'pipe:3',
            ],
        stdin=PIPE, in_3=PIPE) as p:
    p.stdin.write(b'There is nothing special.')
    p.in_3.write(b'\xf0\x9f\x99\x88\xf0\x9f\x99\x89\xf0\x9f\x99\x8a')
4

1 回答 1

3

stderr,stdoutstdin受到特殊对待。当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=Falsepreexec_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:
    ...
于 2013-09-08T18:05:11.753 回答