Python的C程序挂起子AT"在ITER&QUOT线; [英] Python C program subprocess hangs at "for line in iter"

查看:264
本文介绍了Python的C程序挂起子AT"在ITER&QUOT线;的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

好了,所以我想运行一个python脚本的C程序。目前我使用的是测试C程序:

 的#include<&stdio.h中GT;诠释主(){
而(1){
    的printf(2000 \\ n);
    睡眠(1);
}
返回0;
}

要模仿,我将要使用的程序,它从一个传感器读数需要不断。
然后我试图读取输出(在这种情况下,2000)从C程序与蟒蛇子:

 #!USR /斌/蟒蛇
进口子流程= subprocess.Popen(./主,标准输出= subprocess.PIPE)
而真正的:
    在ITER(process.stdout.readline,'')线:
            打印线,

但是这是行不通的。从使用打印语句,它运行那么 .Popen 行以等待线ITER(process.stdout.readline,''),直到我preSS按Ctrl-C。

这是为什么?这正是我所见过的大多数例子作为其code,但它并不读取文件。<​​/ P>

修改

有没有使它运行时,才会有东西要读取的一种方式?


解决方案

这是块缓冲问题。

下面是一个扩展我的回答的Python你的情况的版本:从subprocess.communicate读取流输入()的问题。

在C程序修复标准输出缓冲区直接

STDIO 如果他们在当他们的标准输出重定向到一个管道缓冲的一个终端,交互块基于运行程序作为一项规则都行缓冲。在后一种情况下,你将不会看到新的生产线,直到缓冲区溢出或刷新。

要避免调用 fflush()每个的printf()电话后,你可以通过调用强制行缓冲输出在C程序在开始的时候:

  setvbuf用来(标准输出,(字符*)NULL,_IOLBF,0); / *使行缓冲标准输出* /

一旦一个新行打印缓冲器被刷新在这种情况

或者修复它,而无需修改C程序的源代码

stdbuf 工具,它可以让你改变缓冲型无需修改源$ C ​​$ C例如:

 从子进口POPEN,PIPE流程= POPEN([stdbuf,-oL,./main],标准输出=管,BUFSIZE = 1)
在ITER线(process.stdout.readline,B''):
    打印线,
process.communicate()#关闭进程流,等待它退出

有还提供其他实用程序,请参见关闭缓冲管道

或者使用伪TTY

要欺骗子,以为它在交互的运行,你可以使用 Pexpect的模块或它的类似物,对于使用 Pexpect的 PTY 模块code范例见 Python的子readlines方法()挂起。下面是关于该处提供的 PTY 例子的变化(应该在Linux上工作):

 #!的/ usr /斌/包膜蟒蛇
进口OS
进口PTY
进口SYS
从选择导入选择
从子进口POPEN,STDOUTmaster_fd,slave_fd = pty.openpty()#提供TTY启用行缓冲
流程= POPEN(./主,标准输入= slave_fd,标准输出= slave_fd,标准错误= STDOUT,
                BUFSIZE = 0,close_fds = TRUE)
超时= 0.1#丑陋但除此之外`select`块的过程退出
#code是pty.py类似_copy()
与os.fdopen(master_fd,'R + B',0)作为主:
    input_fds = [大师,sys.stdin]
    而真正的:
        FDS =选择(input_fds,[],[],超时)[0]
        如果主在FDS:#子'输出就绪
            数据= os.read(master_fd,512)#&LT; - 不堵,可能会返回少
            如果没有数据:#EOF
                input_fds.remove(主)
            其他:
                os.write(sys.stdout.fileno(),数据)#复制到我们的标准输出
        如果sys.stdin在FDS:#得到用户输入
            数据= os.read(sys.stdin.fileno(),512)
            如果没有数据:
                input_fds.remove(sys.stdin)
            其他:
                master.write(数据)#把它复制到子过程标准输入
        如果不是FDS:在选择#超时()
            如果process.poll()不无:#子结束
                #没有输出缓冲&LT; - 超时+死子
                断言不选择([主],[],[],0)[0]#比赛是可能的
                os.close(slave_fd)#subproces不需要它了
                打破
RC = process.wait()
打印(子退出,状态%D%RC)

或者使用 PTY 通过 Pexpect的

Pexpect的包裹 PTY 处理成的higher级接口:

 #!的/ usr /斌/包膜蟒蛇
进口Pexpect的孩子= pexpect.spawn(/主)
在子行:
    打印线,
child.close()

问:为什么不直接使用管道(popen()完成)解释了为什么伪-tty是很有用的。

Ok so I'm trying to run a C program from a python script. Currently I'm using a test C program:

#include <stdio.h>

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

To simulate the program that I will be using, which takes readings from a sensor constantly. Then I'm trying to read the output (in this case "2000") from the C program with subprocess in python:

#!usr/bin/python
import subprocess

process = subprocess.Popen("./main", stdout=subprocess.PIPE)
while True:
    for line in iter(process.stdout.readline, ''):
            print line,

but this is not working. From using print statements, it runs the .Popen line then waits at for line in iter(process.stdout.readline, ''):, until I press Ctrl-C.

Why is this? This is exactly what most examples that I've seen have as their code, and yet it does not read the file.

Edit:

Is there a way of making it run only when there is something to be read?

解决方案

It is a block buffering issue.

What follows is an extended for your case version of my answer to Python: read streaming input from subprocess.communicate() question.

Fix stdout buffer in C program directly

stdio-based programs as a rule are line buffered if they are running interactively in a terminal and block buffered when their stdout is redirected to a pipe. In the latter case, you won't see new lines until the buffer overflows or flushed.

To avoid calling fflush() after each printf() call, you could force line buffered output by calling in a C program at the very beginning:

setvbuf(stdout, (char *) NULL, _IOLBF, 0); /* make line buffered stdout */

As soon as a newline is printed the buffer is flushed in this case.

Or fix it without modifying the source of C program

There is stdbuf utility that allows you to change buffering type without modifying the source code e.g.:

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

There are also other utilities available, see Turn off buffering in pipe.

Or use pseudo-TTY

To trick the subprocess into thinking that it is running interactively, you could use pexpect module or its analogs, for code examples that use pexpect and pty modules, see Python subprocess readlines() hangs. Here's a variation on the pty example provided there (it should work on 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)

Or use pty via pexpect

pexpect wraps pty handling into 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())? explains why pseudo-TTY is useful.

这篇关于Python的C程序挂起子AT&QUOT;在ITER&QUOT线;的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
相关文章
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆