Python C程序子进程挂起在“for line in iter”

2024-04-26 13:45:42 发布

您现在位置:Python中文网/ 问答频道 /正文

好的,我试着从一个python脚本运行一个C程序。目前我正在使用一个测试C程序:

#include <stdio.h>

int main() {
while (1) {
    printf("2000\n");
    sleep(1);
}
return 0;
}

为了模拟我将要使用的程序,它不断地从传感器获取读数。 然后,我试图从C程序中读取输出(在本例中为"2000"),其中包含python中的子进程:

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

这是为什么?这正是我看到的大多数示例的代码,但它并没有读取文件。

编辑:

有没有办法让它只在有东西要读的时候运行?


Tags: in程序脚本forreadlineincludemainstdout
3条回答

这是一个块缓冲问题。

下面是我对Python: read streaming input from subprocess.communicate()问题的回答的案例版本的扩展。

直接修复C程序中的stdout缓冲区

基于stdio的程序通常是行缓冲的,如果它们在终端中以交互方式运行,并且当它们的stdout重定向到管道时,块缓冲。在后一种情况下,在缓冲区溢出或刷新之前,不会看到新行。

为了避免在每次调用后调用fflush(),可以通过在一开始调用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

还有其他可用的实用程序,请参见Turn off buffering in pipe

或使用伪TTY

要诱使子进程认为它是交互运行的,可以使用^{} module或其类似代码,对于使用pexpectpty模块的代码示例,请参见Python subprocess readlines() hangs。这里是这里提供的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)

或者通过pexpect

使用pty

pexpectpty处理包装到higher level interface

#!/usr/bin/env python
import pexpect

child = pexpect.spawn("/.main")
for line in child:
    print line,
child.close()

Q: Why not just use a pipe (popen())?解释了伪TTY为什么有用。

readline docs

您的代码:

process.stdout.readline

正在等待EOF或换行。

我不知道你最终想要做什么,但是在printf中添加一个新行,例如printf("2000\n");,至少可以让你开始。

你的程序没有挂起,只是运行得很慢。您的程序正在使用缓冲输出;"2000\n"数据不会立即写入stdout,但最终会成功。在您的情况下,可能需要BUFSIZ/strlen("2000\n")秒(可能是1638秒)才能完成。

在这行之后:

printf("2000\n");

添加

fflush(stdout);

相关问题 更多 >