在 C 或 Python 中使用 popen 绕过子进程输出的缓冲
我有一个关于 popen (和所有相关函数)的一般问题,适用于所有操作系统,当我编写 python 脚本或一些 c 代码并从控制台(win 或 linux)运行生成的可执行文件时,我可以立即看到输出从过程中。但是,如果我运行与分叉进程相同的可执行文件,并将其 stdout 重定向到管道中,则输出会在某处缓冲,通常最多 4096 字节,然后才会写入父进程可以读取的管道。
以下 python 脚本将以 1024 字节为单位生成输出
import os, sys, time
if __name__ == "__main__":
dye = '@'*1024
for i in range (0,8):
print dye
time.sleep(1)
以下 python 脚本将执行前面的脚本,并在输出到达管道时立即逐字节读取输出
import os, sys, subprocess, time, thread
if __name__ == "__main__":
execArgs = ["c:\\python25\\python.exe", "C:\\Scripts\\PythonScratch\\byte_stream.py"]
p = subprocess.Popen(execArgs, bufsize=0, stdout=subprocess.PIPE)
while p.returncode == None:
data = p.stdout.read(1)
sys.stdout.write(data)
p.poll()
调整操作系统的路径。在此配置中运行时,输出不会以 1024 的块出现,而是以 4096 的块出现,尽管 popen 命令的缓冲区大小设置为 0(无论如何这是默认值)。谁能告诉我如何改变这种行为?有什么方法可以强制操作系统以与从控制台运行时相同的方式处理分叉进程的输出?即,只需通过没有缓冲?
I have a general question about popen (and all related functions), applicable to all operating systems, when I write a python script or some c code and run the resulting executable from the console (win or linux), i can immediately see the output from the process. However, if I run the same executable as a forked process with its stdout redirected into a pipe, the output buffers somewhere, usually up to 4096 bytes before it is written to the pipe where the parent process can read it.
The following python script will generate output in chunks of 1024 bytes
import os, sys, time
if __name__ == "__main__":
dye = '@'*1024
for i in range (0,8):
print dye
time.sleep(1)
The following python script will execute the previous script and read the output as soon as it comes to the pipe, byte by byte
import os, sys, subprocess, time, thread
if __name__ == "__main__":
execArgs = ["c:\\python25\\python.exe", "C:\\Scripts\\PythonScratch\\byte_stream.py"]
p = subprocess.Popen(execArgs, bufsize=0, stdout=subprocess.PIPE)
while p.returncode == None:
data = p.stdout.read(1)
sys.stdout.write(data)
p.poll()
Adjust the path for your operating system. When run in this configuration, the output will not appear in chunks of 1024 but chunks of 4096, despite the buffer size of the popen command being set to 0 (which is the default anyway). Can anyone tell me how to change this behaviour?, is there any way I can force the operating system to treat the output from the forked process in the same way as when it is run from the console?, ie, just feed the data through without buffering?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
一般来说,标准 C 运行时库(或多或少代表每个系统上的几乎每个程序运行;-)检测 stdout 是否是终端;如果没有,它会缓冲输出(与未缓冲的输出相比,这可能会带来巨大的效率提升)。
如果您控制正在写入的程序,您可以(正如另一个答案所建议的那样)连续刷新标准输出,或者(如果可行的话更优雅)尝试强制标准输出不缓冲,例如通过使用
运行Python -u
命令行标志:(手册页添加的是标准输入和二进制模式问题的提及)。
如果您不能或不想接触正在写入的程序,则正在读取的程序上的
-u
或类似内容不太可能有帮助(最重要的缓冲是发生在作者的标准输出,而不是读者的标准输入)。另一种方法是通过pty
标准库模块或更高级别的第三方pexpect 模块(或者,对于 Windows,其端口 wexpect)。In general, the standard C runtime library (that's running on behalf of just about every program on every system, more or less;-) detects whether stdout is a terminal or not; if not, it buffers the output (which can be a huge efficiency win, compared to unbuffered output).
If you're in control of the program that's doing the writing, you can (as another answer suggested) flush stdout continuously, or (more elegantly if feasible) try to force stdout to be unbuffered, e.g. by running Python with the
-u
commandline flag:(what the man page adds is a mention of stdin and issues with binary mode[s]).
If you can't or don't want to touch the program that's writing,
-u
or the like on the program that's just reading is unlikely to help (the buffering that matters most is the one happening on the writer's stdout, not the one on the reader's stdin). The alternative is to trick the writer into believing that it's writing to a terminal (even though in fact it's writing to another program!), via thepty
standard library module or the higher-level third party pexpect module (or, for Windows, its port wexpect).这是正确的,并且适用于 Windows 和 Linux(可能还有其他系统),使用
popen()
和fopen()
。如果您希望在 4096 字节之前分派输出缓冲区,请使用fflush()
(在 C 上)或sys.stdout.flush()
(Python)。Thats correct, and applies to both Windows and Linux (and possibly other systems), with
popen()
andfopen()
. If you want the output buffer to be dispatched before 4096 bytes, usefflush()
(on C) orsys.stdout.flush()
(Python).在 C/C++ 中,popen 正在读取子进程。您可以调用 setvbuf 函数:
这会将标准输出设置为非缓冲,并且您的输出将正常工作。通常我会在 main() 进程开始时执行此操作。
我还没有找到任何方法可以从读取管道的主机进程中执行此操作。据推测,必须有一些 fcntl 或 stty 函数来欺骗子进程,使其认为它在终端中。如果有人知道答案,我很想知道。
In C/C++ on the child process being read by popen. You can call into the setvbuf function:
This will set the stdout to non buffering, and your output will work correctly. Typically i do this right at the beginning of the process in main().
I've yet to find any way to do this from the host process reading the pipe. Presumably there must be some fcntl or stty function to trick the child process into thinking its in a terminal. If anyone knows the answer, i'd love to know.