发出CTRL + C命令之前Popen读取数据

时间:2018-06-26 07:58:49

标签: python c

我有一个用C编写的Windows命令行工具,可通过串行USB Dongle扫描蓝牙设备。

它将重复遍历所有设备,直到收到CTRL + C命令为止。

device1 name firmware
device2 name firmware
device3 name firmware
device1 name firmware
device2 name firmware
device3 name firmware
...

我想在扫描到达特定设备时停止扫描,以便发出更新固件命令。

目前,我只能使用下面的函数发出CTRL + C命令后捕获输出,该函数开始扫描,休眠,然后发出CTRL + C命令,然后捕获错误并在except块:

        command = [self.cli_tool, '-s']

        startupinfo = subprocess.STARTUPINFO()
        startupinfo.dwFlags |= subprocess.STARTF_USESHOWWINDOW
        startupinfo.wShowWindow = subprocess.SW_HIDE

        stream = []

        if self.check_dongle_firmware() is not False:
            try:                    
                self.proc = subprocess.Popen(
                            command, 
                            stdin=subprocess.PIPE, 
                            stdout=subprocess.PIPE, 
                            stderr=subprocess.DEVNULL, 
                            startupinfo=startupinfo)

                time.sleep(SCAN_TIMEOUT)
                os.kill(self.proc.pid, signal.CTRL_C_EVENT)
                self.proc.wait()

            except KeyboardInterrupt:                
                for line in self.proc.stdout:
                    stream.append(line)                         

                for x in stream[7:]:
                    x = x.decode()
                    print(x.strip())   

我想要这样的东西:

stream = []

 self.proc = subprocess.Popen(
                            command, stdin=subprocess.PIPE,
                            stdout=subprocess.PIPE, 
                            stderr=subprocess.DEVNULL, 
                            startupinfo=startupinfo)

for line in self.proc.stdout:
    stream.append(line) 
    if 'device2' in line.decode().split():
        os.kill(self.proc.pid, signal.CTRL_C_EVENT)
        self.proc.wait()

但这不起作用。它将不会一次读取1行,并且永远不会到达CTRL + C事件。

我需要一次读取并处理1行,以便可以在特定设备上停止该过程。

当前,它仅读取空字节或字符串,具体取决于管道属性。

打印到屏幕上(关闭启动信息)或保存到文件而无需发出CTRL + C,将显示为空。 我尝试使用:

universal_newlines=True

bufsize=1

self.proc.communicate()[0]

当我使用ping之类的命令尝试代码时,我没有问题,并且可以完全控制输出。

我已经搜索了SO,尝试了所有看似相似但无济于事的事情。

我认为我缺少明显的东西,或者由于该工具未在C代码中应用冲洗命令而无法实现?

任何方向或建议都值得赞赏!

在C代码中,我仅在以下我认为是用于错误处理的位置进行了冲洗。

project.hbootloader\src\common\中都

receiver\src\common\

//*************************************************************************
//------------------------- assertion of errors  --------------------------
//*************************************************************************
/*
 *   The format is assert(eval, error);
 *
 *   If 'eval' equals 0 then there is an error printed with number 'error'.
 *
 */

#define ERR_STACKOVERFLOW 0   // Out of stack space
#define ERR_SCH_OVERFLOW  1   // Scheduler overflow
#define ERR_SCH_OUTRANGE  2   // Scheduler out of range
#define ERR_WSHRS_OUTRANGE  3   // Scheduler out of range

#ifdef __nDEBUG__

#include "./error/error.h"
....
#include <stdio.h>
#define DBG_PRINT(a, args...) { printf(a, ##args);fflush(stdout); }

1 个答案:

答案 0 :(得分:3)

这里的问题可能是CLI程序的输出是块缓冲的,因为输出文件描述符没有与终端关联。您声称它与“ ping”而不是cli程序一起使用,因此,肯定是子流程本身存在问题。

由于您是自己对cli程序进行编程的,因此请确保它不会缓冲输出(即,在每行要打印的内容后放置fflush()

具体来说,调整代码如下:

while (somecondition) {
    ...
    printf("%s %s %s\n", device, name, firmware);
    fflush(stdout); // <- Add this line after the printf
    ...
}

或者,如melpomene建议的那样,您还可以使用以下命令将stdout切换为程序的早期行缓冲:

setvbuf(stdout, NULL, _IOLBF, 0);

Online C11 standard,7.21.3 / 3:

  

当流未缓冲时,字符应尽快从源或目标出现。否则,字符可能会作为块被累积并传输到主机环境或从主机环境传输。当流被充分缓冲时,打算在填充缓冲区时将字符作为块与主机环境进行传输。当流是行缓冲时,当遇到换行符时,字符打算作为块与主机环境进行传输。此外,当填充缓冲区,在无缓冲流上请求输入或在需要从主机环境传输字符的行缓冲流上请求输入时,打算将字符作为块传输到主机环境。 。对这些特性的支持是实现定义的,并且可能会受setbufsetvbuf函数的影响。

7.21.3 / 7:

  

在程序启动时,预定义了三个文本流,无需显式打开它们-标准输入(用于读取常规输入),标准输出(用于写入常规输出)和标准错误(用于写入诊断输出)。最初打开时,标准错误流未完全缓冲;仅当可以确定标准输入和标准输出流不引用交互式设备时,标准输入流和标准输出流才被完全缓冲。