PC System: Ubuntu 18.04
Python version: 3.6.9
When execute command like "adb logcat" by subprocess.Popen, I want to get the stdout and decide whether to stop the subprocess based on the keyword in it. If the keyword does not appear for a long time, i will stop it too. The first attempt is as follows
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
The code succeeded in doing what I need, but I found that if there is no next output from the tenth seconds after the subprocess starts, the program will not end with "Fail." until the next output.
I think it's because readline()
blocks to read the output. So, i set the stdout to non-block by fcntl
like the following code
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
As shown above, the results were as expected. However, it execute readline()
4131304 times in 10 seconds, It may waste resources, and the fcntl
module cannot be used on Windows.
So, is there a more elegant, generic way to implement this requirement?
You could maybe in every iteration call an async function which will kill the process after 10 secs, and also in every iteration kill the previous async call, like
killer_coroutine = None
while loop:
# stuff...
if killer_coroutine:
killer_coroutine.cancel()
killer_coroutine = async_func()
so if the loop stops at some point, the process will get killed. I am not sure if this would work, maybe worth a shot.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.