好的,所以我试图从python脚本运行C程序。当前,我正在使用测试C程序:

#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。

这是为什么?这正是我所看到的大多数示例作为其代码的内容,但是却无法读取文件。

编辑:

是否有一种制作方法仅在有需要阅读的内容时才运行?

评论

在True时下降。一旦for循环结束;您将再也无法从process.stdout中得到任何东西。

我认为答案应该改为J.F. Sebastians版本。在那里查看我的评论。

谢谢@Scheintod,很好。

#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模块或其类似物,有关使用pexpectpty模块的代码示例,请参见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


pexpectpty处理包装到更高级别的接口中:

#!/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");,应该至少可以帮助您入门。