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
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 ...
).
– bitinerant
Dec 31 '20 at 20:27
tail -f
on a non-empty file, so it did output something initially.
– Stéphane Chazelas
Jan 01 '21 at 09:24
rm -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; }
– bitinerant
Jan 01 '21 at 16:56
-f
flag. But by see edit, I meant the socat part and below to handle inactivity timeout.
– Stéphane Chazelas
Jan 01 '21 at 18:27
exec tail -f ...
, I can't get the Bash version to work (doesn't exit). The socat
option looks useful.
– bitinerant
Jan 01 '21 at 19:20
With 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.
– Kusalananda
Dec 31 '20 at 11:00
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? – steeldriver Dec 31 '20 at 02:20|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. – bitinerant Dec 31 '20 at 05:38