#include <stdio.h>
int main() {
while (1) {
printf("2000\n");
sleep(1);
}
return 0;
}
要模拟我将要使用的程序,该程序会不断从传感器读取读数。
然后我正在尝试从python中带有子进程的C程序读取输出(在本例中为
"2000"
):#!usr/bin/python
import subprocess
process = subprocess.Popen("./main", stdout=subprocess.PIPE)
while True:
for line in iter(process.stdout.readline, ''):
print line,
,但这不起作用。从使用print语句开始,它运行
.Popen
行,然后在for line in iter(process.stdout.readline, ''):
处等待,直到我按Ctrl-C。这是为什么?这正是我所看到的大多数示例作为其代码的内容,但是却无法读取文件。
编辑:
是否有一种制作方法仅在有需要阅读的内容时才运行?
#1 楼
这是一个块缓冲问题。下面是对我的Python回答的案例版本的扩展:从subprocess.communicate()问题中读取流输入。
修复直接在C程序中的stdout缓冲区
如果基于
stdio
的程序在终端中以交互方式运行,则通常会对其进行行缓冲,而在将其stdout重定向至管道时则对块进行缓冲。在后一种情况下,您不会看到新行,直到缓冲区溢出或刷新为止。为避免在每次
fflush()
调用后调用printf()
,可以通过在C程序中调用C来强制行缓冲输出。一开始:setvbuf(stdout, (char *) NULL, _IOLBF, 0); /* make line buffered stdout */
在这种情况下,一旦换行符被打印,缓冲区就会被刷新。
或者在不修改源代码的情况下对其进行修复。 C程序的代码
有
stdbuf
实用程序,可让您更改缓冲类型而无需修改源代码,例如:from subprocess import Popen, PIPE
process = Popen(["stdbuf", "-oL", "./main"], stdout=PIPE, bufsize=1)
for line in iter(process.stdout.readline, b''):
print line,
process.communicate() # close process' stream, wait for it to exit
还有其他可用的实用程序,请参阅关闭管道中的缓冲。
或使用伪TTY
若要使子进程认为其正在交互运行,可以使用
pexpect
模块或其类似物,有关使用pexpect
和pty
模块的代码示例,请参见Python子进程readlines()挂起。以下是此处提供的pty
示例的一种变体(它应在Linux上运行):#!/usr/bin/env python
import os
import pty
import sys
from select import select
from subprocess import Popen, STDOUT
master_fd, slave_fd = pty.openpty() # provide tty to enable line buffering
process = Popen("./main", stdin=slave_fd, stdout=slave_fd, stderr=STDOUT,
bufsize=0, close_fds=True)
timeout = .1 # ugly but otherwise `select` blocks on process' exit
# code is similar to _copy() from pty.py
with os.fdopen(master_fd, 'r+b', 0) as master:
input_fds = [master, sys.stdin]
while True:
fds = select(input_fds, [], [], timeout)[0]
if master in fds: # subprocess' output is ready
data = os.read(master_fd, 512) # <-- doesn't block, may return less
if not data: # EOF
input_fds.remove(master)
else:
os.write(sys.stdout.fileno(), data) # copy to our stdout
if sys.stdin in fds: # got user input
data = os.read(sys.stdin.fileno(), 512)
if not data:
input_fds.remove(sys.stdin)
else:
master.write(data) # copy it to subprocess' stdin
if not fds: # timeout in select()
if process.poll() is not None: # subprocess ended
# and no output is buffered <-- timeout + dead subprocess
assert not select([master], [], [], 0)[0] # race is possible
os.close(slave_fd) # subproces don't need it anymore
break
rc = process.wait()
print("subprocess exited with status %d" % rc)
或通过
pty
使用pexpect
pexpect
将pty
处理包装到更高级别的接口中:#!/usr/bin/env python
import pexpect
child = pexpect.spawn("/.main")
for line in child:
print line,
child.close()
问:为什么不只使用管道(popen())?解释了为什么伪TTY有用。
评论
我认为此答案应标记为正确,因为它是最完整,简洁的内容,并提供各种可能的解决方案和进一步的阅读材料。 (这是一个很好的答案。)但是我真的认为,为了使读者能够处理这些密集的信息,在不同方法之间绝对需要某种标题。我认为大多数人会看到大的python块,并在意识到它只是许多可能的解决方案之一之前就被吓到了。
– Scheintod
2013年12月28日上午10:01
@Scheintod:我添加了部分标题。老实说:我已经回答了这个问题,因为我想和pty一起玩,选择。顺便说一句,如果您的答案没有明显变差,则可以自己编辑。
– jfs
2013年12月28日下午14:41
您是否知道Windows上类似的输出缓冲解决方案?
– Kuchi
17年12月12日在16:10
@kuchi我不知道
– jfs
17年12月12日在20:17
@kuchi看看pywinpty。我不知道这是否回答了您的问题,但是它似乎在执行pty之类的操作,但适用于Windows。
–flutefreak7
19 Mar 7 '19 at 8:03
#2 楼
您的程序没有挂起,只是运行非常缓慢。您的程序正在使用缓冲输出;"2000\n"
数据不会立即写入stdout,但最终会写入。您的情况可能需要BUFSIZ/strlen("2000\n")
秒(大约1638秒)才能完成。 此行后:
printf("2000\n");
添加
fflush(stdout);
评论
非常感谢你!那就是问题所在
–avidreader610
2013年12月10日20:18
谢谢Rob,我只是想补充一点,以便他可以更快地看到它。 @ theoB610,请注意,您仍然需要换行符。
–编码器
2013年12月10日20:25
非常真实!谢谢@codnodder
–avidreader610
2013年12月10日20:30
#3 楼
请参阅readline文档。您的代码:
process.stdout.readline
正在等待EOF或换行符。
我不能告诉您最终要做什么,但是至少在您的printf中添加换行符,例如
printf("2000\n");
,应该至少可以帮助您入门。
评论
在True时下降。一旦for循环结束;您将再也无法从process.stdout中得到任何东西。我认为答案应该改为J.F. Sebastians版本。在那里查看我的评论。
谢谢@Scheintod,很好。