4

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

4
  • 1
    Does your system provide the timeout command? See for example How to introduce timeout for shell scripting?CommentedDec 31, 2020 at 2:20
  • I have the timeout command but I want the script to run forever if it is still outputting stuff to the terminal so I cannot simply just set a timeout of 2 minutesCommentedDec 31, 2020 at 4:28
  • It's not elegant, but you could add |tee /tmp/pythoncode_out at the end your python3 command, and then have another loop than checks every 15 seconds and kills the bash processes running the loop if pythoncode_out is more than 2 minutes old. In theory, one could write a program to do what you ask, killing python3 just like head kills its source process if the given conditions were met.CommentedDec 31, 2020 at 5:38
  • you might be able to nest it in the python scriptCommentedDec 31, 2020 at 18:04

4 Answers 4

7

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 } 
6
  • Looks beautiful, but the Bash version isn't working for me (it never exits; my line 3 was exec tail -f ...).CommentedDec 31, 2020 at 20:27
  • Thank you StephaneCommentedJan 1, 2021 at 4:40
  • @bitinerant, see edit. I suspect you ran tail -f on a non-empty file, so it did output something initially.CommentedJan 1, 2021 at 9:24
  • Thank you for your work, Stéphane. However, on Bash 5.0, the new version exits immediately: 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; }CommentedJan 1, 2021 at 16:56
  • @bitinerant, you missed the -f flag. But by see edit, I meant the socat part and below to handle inactivity timeout.CommentedJan 1, 2021 at 18:27
3

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.

4
  • Thanks for your reply. Context: My python script sometimes freezes cuz it's internet based. If it hasn't outputted anything in 2 min it means that it is frozen and it will never output anything again. Instead of waiting for me to find out that it's frozen and restarting it, I want it to happen automatically. Correct me if I'm wrong but this would not work for my situation because: If my python hasn't outputted in 2 min it means that is frozen and it will never output anything again so the problem of it being frozen will remain forever.CommentedJan 1, 2021 at 4:43
  • You may want to read Understanding "IFS= read -r line"CommentedJan 1, 2021 at 9:26
  • @ArdalanGhazizadeh if the script is inactive for 2 minutes, it will be terminated.
    – thanasisp
    CommentedJan 1, 2021 at 12:06
  • Excellent. IMHO, your answer is better than Stephane's.
    – ttsiodras
    CommentedJan 7, 2021 at 14:41
0

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 
3
  • The assignment to OUT will not finish until the Python script exits.
    – Kusalananda
    CommentedDec 31, 2020 at 11:00
  • Correct, but I suppose that the author's script evaluates something, occasionally prints on the console, then terminates and is runed again, since the author wrapped it in an infinite loop. If @Ardalan could explain better his script behaviour we could understand better.
    – Maik93
    CommentedDec 31, 2020 at 11:28
  • The issue seems to be that the Python script needs to be killed if it doesn't output anything during a certain amount of time.
    – Kusalananda
    CommentedDec 31, 2020 at 12:10
0

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.

    You must log in to answer this question.

    Start asking to get answers

    Find the answer to your question by asking.

    Ask question

    Explore related questions

    See similar questions with these tags.