如何从子流程获取stdout.优雅地弹出超时检查



PC系统:Ubuntu 18.04
Python版本:3.6.9
当执行类似"adb-logcat";按子流程。Popen,我想获取stdout,并根据其中的关键字决定是否停止子流程。如果这个关键词长时间不出现,我也会停止它。第一次尝试如下

import time
import subprocess
cmd = "adb logcat"
timeout = 10
adb_shell_pipe = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE, universal_newlines=True)
start_time = time.time()
for info in iter(adb_shell_pipe.stdout.readline, ""):
if "keyword" in info:
print("Find keyword!")
# Omit kill process
break
if time.time() - start_time > timeout:
print("Fail!")
# Omit kill process
break

代码成功地完成了我需要的操作,但我发现,如果在子流程启动后的第十秒没有下一个输出,程序将不会以"0"结束;失败"直到下一次输出
我认为这是因为readline()阻止读取输出。因此,我通过fcntl将stdout设置为非块,就像下面的代码一样

import os
import time
import fcntl
import subprocess
# Command was replaced to create test scenarios
cmd = "adb shell"
timeout = 10
adb_shell_pipe = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE, universal_newlines=True)
fd = adb_shell_pipe.stdout.fileno()
fl = fcntl.fcntl(fd, fcntl.F_GETFL)
fcntl.fcntl(fd, fcntl.F_SETFL, fl | os.O_NONBLOCK)
count = 0
start_time = time.time()
while adb_shell_pipe.poll() is None:
count += 1
info = adb_shell_pipe.stdout.readline()
if info and "keyword" in info:
print("Find keyword!")
# Omit kill process
break
if time.time() - start_time > timeout:
print("Fail!")
# Omit kill process
break
print(count)
# Output
Fail!
4131304

如上所示,结果如预期。但是,它在10秒内执行readline()4131304次,可能会浪费资源,并且fcntl模块不能在Windows上使用
那么,有没有一种更优雅、通用的方法来实现这一需求呢?

您可以在每次迭代中调用一个异步函数,该函数将在10秒后终止进程,也可以在每次重复中终止前一个异步调用,如

killer_coroutine = None
while loop:
# stuff...
if killer_coroutine:
killer_coroutine.cancel()
killer_coroutine = async_func()

因此,如果循环在某个时刻停止,进程将被终止。我不确定这是否可行,也许值得一试。

相关内容

  • 没有找到相关文章

最新更新