在发出 CTRL+C 命令之前打开读取数据



我有一个用 C 语言编写的命令行工具,用于 Windows,该工具通过串行 USB 加密狗扫描蓝牙设备。

它将重复遍历范围内的所有设备,直到收到 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 代码中,我只在以下位置有 fflush,我认为这些位置用于错误处理。

project.hbootloadersrccommonreceiversrccommon

//*************************************************************************
//------------------------- 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); }

这里的问题可能是 CLI 程序的输出是块缓冲的,因为输出文件描述符与终端无关。您声称它适用于"ping"而不是您的 cli 程序,因此它肯定是子进程本身的问题。

由于您自己编写了 cli 程序,请确保它不会缓冲输出(即在打印的每一行之后放置一个fflush()

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

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

或者,正如melpomene建议的那样,您也可以在程序早期通过以下方式切换标准输出以进行行缓冲:

setvbuf(stdout, NULL, _IOLBF, 0);

在线 C11 标准,7.21.3/3:

当流未缓冲时,字符旨在尽快从源或目标显示。否则,字符可能会作为块累积并传输到主机环境或从主机环境传输。当流完全缓冲时,字符旨在作为缓冲区填充时的块传输到主机环境或从主机环境传输。当流进行行缓冲时,字符旨在在遇到换行符时作为块传输到主机环境或从主机环境传输。此外,当缓冲区被填满时,当在未缓冲的流上请求输入时,或者在需要从主机环境传输字符的行缓冲流上请求输入时,字符旨在作为块传输到主机环境。对这些特征的支持是实现定义的,可能会受到setbufsetvbuf功能的影响。

7.21.3/7:

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

最新更新