Python subprocess模块:父子进程通信不正常

2 投票
2 回答
1797 浏览
提问于 2025-04-16 21:05

我正在尝试将以下代码作为一个子进程运行

#include<stdio.h>
int main()
{
    int a;
    printf("Hello\n");
    fprintf(stderr, "Hey\n");
    scanf("%d", &a);
    printf("%d\n", a);
    return 0;
}

这个脚本运行得很好:可以写入标准输入(stdin),从标准输出(stdout)和标准错误(stderr)中读取。

#!/usr/bin/python

import subprocess

p1=subprocess.Popen("/mnt/test/a.out", stdout=subprocess.PIPE, stdin=subprocess.PIPE, stderr=subprocess.PIPE)

p1.stdin.write('1\n')
print p1.stdout.readline()
print p1.stderr.readline()
print p1.stdout.readline()

但是这个脚本无法从标准输出中读取任何内容,并且在这里被阻塞,尽管C程序在要求输入之前确实会打印到标准输出。为什么我无法从标准输出中读取到任何东西呢?

#!/usr/bin/python

import subprocess

p1=subprocess.Popen("/mnt/test/a.out", stdout=subprocess.PIPE, stdin=subprocess.PIPE, stderr=subprocess.PIPE)

print p1.stdout.readline()
p1.stdin.write('1\n')
print p1.stderr.readline()
print p1.stdout.readline()

2 个回答

0

我在你的代码里没看到类似这样的东西:

stdout_data, stderr_data = p1.communicate()

在这里,Popen.communicate(input=None) 是一个用来和进程互动的方法。它的作用是:发送数据到进程的输入(stdin),从进程的输出(stdout)和错误输出(stderr)读取数据,直到文件结束。然后,它会等待这个进程结束。可选的输入参数应该是一个字符串,用来发送给子进程,如果不需要发送数据,就填None。

这个方法会返回一个元组,包含两个部分(stdoutdata, stderrdata)。

需要注意的是,如果你想往进程的输入里发送数据,你需要在创建Popen对象的时候设置stdin=PIPE。同样的,如果你想在返回的元组中得到除了None以外的内容,也需要设置stdout=PIPE和/或stderr=PIPE。

还有一点要注意,读取的数据会在内存中缓存,所以如果数据量很大或者没有限制,就不要使用这个方法。

详细信息可以查看 docs.python.org

我有一个函数,常常用来简化调用外部程序的过程,使用subprocess模块。你可以根据自己的需要进行修改:

def __run(self, cmd):
    """wrapper, makes it easy to call an external program.
    return the result as a newline-separated list
    """

    args = shlex.split(cmd)
    try:
        p = subprocess.Popen(args, stdout=subprocess.PIPE,
                             stderr=subprocess.STDOUT)
        retdata = p.communicate()[0]
        p.wait()
    except OSError, e:
        print >>sys.stderr, "Execution failed:", e

    return (p.returncode, retdata.split('\n'))

只需把你的命令像在命令行中那样写在一个变量里,然后调用这个函数,例如:

cmd = r'git branch -r'
data = self.__run(cmd)
2

你需要先刷新这个流。这是为了确保所有的数据都被真正写入到流里面。

#include<stdio.h>
int main()
{
    int a;
    printf("Hello\n");
    fprintf(stderr, "Hey\n");
    fflush(stdout); // <--
    scanf("%d", &a);
    printf("%d\n", a);
    return 0;
}

默认情况下,stderr(标准错误输出)是没有缓冲的,所以你不需要去刷新它。而stdout(标准输出)是完全缓冲的,除非它指向一个终端,这时候它就是行缓冲的(也就是说,遇到\n时会自动刷新)。

你可以在这里查看关于setbuf()和setvbuf()的内容。

撰写回答