subprocess.Popen.stdout - 实时读取 stdout (再次)
又是同样的问题。
原因是 - 在阅读了以下内容后,我仍然无法让它工作:
我的情况是,我有一个用C写的控制台应用程序,举个例子,这段代码在一个循环中:
tmp = 0.0;
printf("\ninput>>");
scanf_s("%f",&tmp);
printf ("\ninput was: %f",tmp);
它不断读取一些输入并写出一些输出。
我用来与它交互的Python代码如下:
p=subprocess.Popen([path],stdout=subprocess.PIPE,stdin=subprocess.PIPE)
p.stdin.write('12345\n')
for line in p.stdout:
print(">>> " + str(line.rstrip()))
p.stdout.flush()
到目前为止,每当我从 p.stdout
读取时,它总是要等到进程结束后才输出一个空字符串。我尝试了很多方法 - 但结果还是一样。
我试过Python 2.6和3.1,但版本并不重要 - 我只需要在某个地方让它工作。
5 个回答
这里的 bufsize=256
参数是用来限制数据发送给子进程的大小的,它确保像 12345\n
这样的数据不会被分成小于256字节的块发送。如果不设置 bufsize
或者在 p.stdin.write()
之后加上 p.stdin.flush()
,数据可能会被分成更小的块发送。默认情况下,数据是按行缓冲的。
无论哪种情况,你至少应该能看到一个空行,这个空行是由你示例中的第一个 printf(\n...)
输出的。
把从管道里读取的数据放到一个单独的线程中,这样当有一部分输出可用时,就会发出信号通知你:
在和子进程进行数据读写时,使用管道会比较复杂,因为默认情况下数据在两个方向上都有缓冲。这很容易导致死锁的情况,比如父进程或子进程在读取一个空的缓冲区,或者在写入一个已经满的缓冲区,或者在等待数据的情况下进行阻塞读取,而这个缓冲区还没有被系统库刷新。
如果数据量不大,Popen.communicate()
方法可能就足够用了。不过,如果数据量超过了它的缓冲能力,你可能会遇到进程停滞的问题(这可能就是你现在看到的情况?)
你可以查查如何使用 fcntl
模块,把你的文件描述符设置为非阻塞模式。这样的话,你就需要在读取和写入这些文件描述符时,添加适当的异常处理来应对“EWOULDBLOCK”事件。(我不记得在Python中具体会抛出什么异常了)。
另外一种完全不同的方法是,让父进程使用 select
模块和 os.fork()
,然后让子进程在处理完文件复制后,使用 execve()
来执行目标程序。(基本上你会重新实现 Popen()
的部分功能,但处理父进程的文件描述符(管道)时会有所不同。)
顺便提一下,在Python的2.5和2.6标准库中,.communicate
方法只能处理大约64K的远程数据(在Linux和FreeBSD上)。这个数字可能会因为各种因素而变化(可能包括编译你的Python解释器时使用的构建选项,或者链接的libc版本)。它并不是简单地受到可用内存的限制(尽管J.F. Sebastian曾声称相反),而是限制在一个更小的值上。