简体   繁体   English

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

[英]Popen read data before CTRL+C command is issued

I have a command line tool written in C for windows that scans for bluetooth devices via a serial USB Dongle. 我有一个用C编写的命令行工具,用于通过串行USB Dongle扫描蓝牙设备的Windows。

It will loop through all devices in range repeating until it receives a CTRL+C command. 它将遍历范围重复的所有设备,直到它收到CTRL + C命令。

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

I want to stop the scan when it reaches a certain device so I can issue an update firmware command. 我想在到达某个设备时停止扫描,这样我就可以发出更新固件命令。

At the moment I can only capture the output after the CTRL+C command is issued using the following function that starts the scan, sleeps, then issues the CTRL+C command, I then catch the error and process the output in the except block: 目前我只能在使用以下启动扫描,睡眠,然后发出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())   

I want something like this: 我想要这样的东西:

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()

This does not work though. 但这不起作用。 It will not read 1 line at a time and the CTRL+C event is never reached. 它不会一次读取1行,并且永远不会达到CTRL + C事件。

I need to read and process 1 line at a time so that i can stop the process at a certain device. 我需要一次读取和处理1行,以便我可以在某个设备上停止该过程。

Currently it only reads an empty byte or string depending on the pipe attributes. 目前,它只根据管道属性读取空字节或字符串。

printing to the screen (With the startup info off) or saving to a file without issuing a CTRL+C will render empty. 打印到屏幕(关闭启动信息)或保存到文件而不发出CTRL + C将呈现为空。 I tried various combinations using : 我试过各种组合使用:

universal_newlines=True

bufsize=1

self.proc.communicate()[0]

When I try the code using a command like ping I have no problem and full control of the output. 当我使用像ping这样的命令尝试代码时,我没有问题并完全控制输出。

I have searched SO for tried anything that looks similar but nothing works for what I need. 我搜索了SO,尝试了一些看似相似的东西,但没有任何东西可以满足我的需求。

I think I am missing something obvious or it is not possible due to the tool not applying a flush command in the C code? 我想我遗漏了一些明显的东西,或者由于该工具没有在C代码中应用flush命令而无法实现?

Any direction or advice is appreciated! 任何方向或建议表示赞赏!

In the C code I have fflush only in the following locations which i presume to be for error handling. 在C代码中,我只在以下位置进行fflush,我认为这些位置用于错误处理。

project.h in both bootloader\\src\\common\\ and receiver\\src\\common\\ 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); }

The problem here is probably, that the output of your CLI program is block buffered, since the output file descriptor is not associated with a terminal. 这里的问题可能是,CLI程序的输出是块缓冲的,因为输出文件描述符与终端没有关联。 You claim, that it works with "ping" instead of your cli program, so it definitively has to be an issue with the subprocess itself. 你声称,它与“ping”而不是你的cli程序一起工作,所以它肯定是子进程本身的一个问题。

Since you programmed the cli program yourself, make sure that it does not buffer the output (ie put an fflush() after each line which is printed. 由于您自己编写了cli程序,因此请确保它不会缓冲输出(即在每行打印后放置一个fflush()

Specifically, adjust the code looks like this: 具体来说,调整代码如下所示:

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

Alternatively, as melpomene suggested, you can also switch stdout to be line buffered early in the program with: 或者,正如melpomene建议的那样,你也可以在程序的早期将stdout切换为行缓冲:

setvbuf(stdout, NULL, _IOLBF, 0);

Online C11 standard , 7.21.3/3: 在线C11标准 ,7.21.3 / 3:

When a stream is unbuffered , characters are intended to appear from the source or at the destination as soon as possible. 当流未缓冲时 ,字符应尽快从源或目的地出现。 Otherwise characters may be accumulated and transmitted to or from the host environment as a block. 否则,可以将字符作为块累积并发送到主机环境或从主机环境发送。 When a stream is fully buffered , characters are intended to be transmitted to or from the host environment as a block when a buffer is filled. 当流被完全缓冲时 ,当填充缓冲区时,字符将作为块传输到主机环境或从主机环境传输。 When a stream is line buffered , characters are intended to be transmitted to or from the host environment as a block when a new-line character is encountered. 当流被线缓冲时 ,当遇到换行符时 ,字符将作为块传输到主机环境或从主机环境传输。 Furthermore, characters are intended to be transmitted as a block to the host environment when a buffer is filled, when input is requested on an unbuffered stream, or when input is requested on a line buffered stream that requires the transmission of characters from the host environment. 此外,当填充缓冲区,在无缓冲流上请求输入时,或者在需要从主机环境传输字符的行缓冲流上请求输入时,字符旨在作为块传输到主机环境。 。 Support for these characteristics is implementation-defined, and may be affected via the setbuf and setvbuf functions. 对这些特性的支持是实现定义的,可能会受到setbufsetvbuf函数的影响。

7.21.3/7: 7.21.3 / 7:

At program startup, three text streams are predefined and need not be opened explicitly — standard input (for reading conventional input), standard output (for writing conventional output), and standard error (for writing diagnostic output). 在程序启动时,预定义了三个文本流,无需明确打开 - 标准输入 (用于读取常规输入), 标准输出 (用于写入常规输出)和标准错误 (用于写入诊断输出)。 As initially opened, the standard error stream is not fully buffered; 最初打开时,标准错误流未完全缓冲; the standard input and standard output streams are fully buffered if and only if the stream can be determined not to refer to an interactive device. 当且仅当可以确定流不参考交互设备时,标准输入和标准输出流被完全缓冲。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM