Python subprocess模块:父子进程通信不正常
我正在尝试将以下代码作为一个子进程运行
#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 个回答
我在你的代码里没看到类似这样的东西:
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)
你需要先刷新这个流。这是为了确保所有的数据都被真正写入到流里面。
#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()的内容。