Here's my current bash which is just a loop
while true ; do python3 /Users/Name/Desktop/pythoncode.py done
I want to terminate pythoncode.py
if it doesn’t output anything to the terminal in 2 min
With zsh
:
zmodload zsh/system ( echo $sysparam[pid] exec python3 /Users/Name/Desktop/pythoncode.py ) | { read pid if sysread -o 1 -s 1 -t120; then cat else kill -s PIPE $pid 2> /dev/null fi }
With bash
, you can do something similar with:
( echo "$BASHPID" exec python3 /Users/Name/Desktop/pythoncode.py ) | { read pid if LC_ALL=C IFS= read -rd '' -n1 -t120 byte; then if [ -n "$byte" ]; then printf %s "$byte" else printf '\0' fi cat else kill -s PIPE "$pid" 2> /dev/null fi }
Those check whether that script writes anything to stdout, not specifically to the terminal.
If you also want to stop if the command has written something, but stopped and hasn't output anything within two minutes, or in other words, kill the command after 2 minutes of inactivity, then you could just use socat
:
socat -T120 'exec:python3 /Users/Name/Desktop/pythoncode.py' -
Or with zsh
:
zmodload zsh/system ( echo $sysparam[pid] exec python3 /Users/Name/Desktop/pythoncode.py ) | { read pid while sysread -o 1 -t 120; ret=$? (( ret == 0 )) do continue; done # 5 means EOF (( ret == 5 )) || kill $pid }
exec tail -f ...
).CommentedDec 31, 2020 at 20:27tail -f
on a non-empty file, so it did output something initially.CommentedJan 1, 2021 at 9:24rm -f /tmp/watched_file; touch $_; ( echo "$BASHPID"; exec tail /tmp/watched_file; ) | { read pid; if LC_ALL=C IFS= read -rd '' -n1 -t15 byte; then if [ -n "$byte" ]; then printf %s "$byte"; else printf '\0'; fi; cat; else kill -s PIPE "$pid" 2> /dev/null; fi; }
CommentedJan 1, 2021 at 16:56-f
flag. But by see edit, I meant the socat part and below to handle inactivity timeout.CommentedJan 1, 2021 at 18:27With bash
, you can use process substitution and redirection, and a while
loop with read
-t
timeout. The subshell will be no more, when the while
loop exits because of the timeout (That means when the python script has no output, seems inactive, for 2 minutes).
while IFS= read -r -t120 x; do printf "%s\n" "$x" done < <(python3 script.py)
Note: I have assumed line-oriented output into this example (or else read
and printf
char by char).
The difference of this structure to using a pipe, is that we have one command here. When this command exits, the file input from the file descriptor is over, the command in the subshell writing into this descriptor is over. So the python script is killed just after the timeout.
When piping like this:
python3 script.py | { while IFS= read -r -t120 x; do printf "%s\n" "$x" done }
we execute two commands, and if the second one exits (because of the timeout), the first one will continue to run, until trying to write to the pipe again. When this happens, it will terminate, without writing anything more, because of broken pipe. So here the python script is killed when trying to write again after the timeout.
If the acceptable accuracy is ~1 second, you could use date +%s
to collect the actual time and check how long there's no output from your script (collecting it as a shell variable):
ELAPSED_T=0 # the time that has passed since last output LAST_OUT_T=`date +%s` # avoid to stop if the first iteration has empty output while [ "$ELAPSED_T" -lt 120 ]; do # continue if the last output was printed less than 120 seconds ago OUT=`python3 /Users/Name/Desktop/pythoncode.py` # run the script and collect the output echo "$OUT" # print the output on screen if [ -n "$OUT" ]; then # if the output is not empty, update the last output time LAST_OUT_T=`date +%s` else # otherwise, evaluate how much time has passed since last output NOW_T=`date +%s` ELAPSED_T=$((NOW_T - LAST_OUT_T)) fi done
OUT
will not finish until the Python script exits.You might want to check out the GNU timeout
command. It takes the duration as an argument followed by the command. Here is a simple invocation:
timeout 5s ping www.website.com
From its manpage:
TIMEOUT(1) User Commands TIMEOUT(1) NAME timeout - run a command with a time limit SYNOPSIS timeout [OPTION] DURATION COMMAND [ARG]... timeout [OPTION] DESCRIPTION Start COMMAND, and kill it if still running after DURATION. ...
You can also specify the type of signal to send to the process (like HUP, 9, SIGTERM, etc.) at the end of the timeout.
HTH.
timeout
command? See for example How to introduce timeout for shell scripting?|tee /tmp/pythoncode_out
at the end yourpython3
command, and then have another loop than checks every 15 seconds and kills the bash processes running the loop ifpythoncode_out
is more than 2 minutes old. In theory, one could write a program to do what you ask, killing python3 just likehead
kills its source process if the given conditions were met.