Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Timeout a command in bash without unnecessary delay

This answer to Command line command to auto-kill a command after a certain amount of time

proposes a 1-line method to timeout a long-running command from the bash command line:

( /path/to/slow command with options ) & sleep 5 ; kill $! 

But it's possible that a given "long-running" command may finish earlier than the timeout.
(Let's call it a "typically-long-running-but-sometimes-fast" command, or tlrbsf for fun.)

So this nifty 1-liner approach has a couple of problems.
First, the sleep isn't conditional, so that sets an undesirable lower bound on the time taken for the sequence to finish. Consider 30s or 2m or even 5m for the sleep, when the tlrbsf command finishes in 2 seconds — highly undesirable.
Second, the kill is unconditional, so this sequence will attempt to kill a non-running process and whine about it.

So...

Is there a way to timeout a typically-long-running-but-sometimes-fast ("tlrbsf") command that

  • has a bash implementation (the other question already has Perl and C answers)
  • will terminate at the earlier of the two: tlrbsf program termination, or timeout elapsed
  • will not kill non-existing/non-running processes (or, optionally: will not complain about a bad kill)
  • doesn't have to be a 1-liner
  • can run under Cygwin or Linux

... and, for bonus points

  • runs the tlrbsf command in the foreground
  • any 'sleep' or extra process in the background

such that the stdin/stdout/stderr of the tlrbsf command can be redirected, same as if it had been run directly?

If so, please share your code. If not, please explain why.

I have spent awhile trying to hack the aforementioned example but I'm hitting the limit of my bash skills.

like image 682
system PAUSE Avatar asked Mar 26 '09 23:03

system PAUSE


People also ask

How do I set bash timeout?

proposes a 1-line method to timeout a long-running command from the bash command line: ( /path/to/slow command with options ) & sleep 5 ; kill $! But it's possible that a given "long-running" command may finish earlier than the timeout.

How do I sleep in bash?

How to Use the Bash Sleep Command. Sleep is a very versatile command with a very simple syntax. It is as easy as typing sleep N . This will pause your script for N seconds, with N being either a positive integer or a floating point number.

How do I set timeout in Linux?

The syntax for the timeout command is as follows: timeout [OPTIONS] DURATION COMMAND [ARG]… The DURATION can be a positive integer or a floating-point number, followed by an optional unit suffix: s - seconds (default)

What is $$ bash?

$$ is a Bash internal variable that contains the Process ID (PID) of the shell running your script. Sometimes the $$ variable gets confused with the variable $BASHPID that contains the PID of the current Bash shell.


2 Answers

You are probably looking for the timeout command in coreutils. Since it's a part of coreutils, it is technically a C solution, but it's still coreutils. info timeout for more details. Here's an example:

timeout 5 /path/to/slow/command with options 
like image 174
yingted Avatar answered Oct 02 '22 02:10

yingted


I think this is precisely what you are asking for:

http://www.bashcookbook.com/bashinfo/source/bash-4.0/examples/scripts/timeout3

#!/bin/bash # # The Bash shell script executes a command with a time-out. # Upon time-out expiration SIGTERM (15) is sent to the process. If the signal # is blocked, then the subsequent SIGKILL (9) terminates it. # # Based on the Bash documentation example.  # Hello Chet, # please find attached a "little easier"  :-)  to comprehend # time-out example.  If you find it suitable, feel free to include # anywhere: the very same logic as in the original examples/scripts, a # little more transparent implementation to my taste. # # Dmitry V Golovashkin <[email protected]>  scriptName="${0##*/}"  declare -i DEFAULT_TIMEOUT=9 declare -i DEFAULT_INTERVAL=1 declare -i DEFAULT_DELAY=1  # Timeout. declare -i timeout=DEFAULT_TIMEOUT # Interval between checks if the process is still alive. declare -i interval=DEFAULT_INTERVAL # Delay between posting the SIGTERM signal and destroying the process by SIGKILL. declare -i delay=DEFAULT_DELAY  function printUsage() {     cat <<EOF  Synopsis     $scriptName [-t timeout] [-i interval] [-d delay] command     Execute a command with a time-out.     Upon time-out expiration SIGTERM (15) is sent to the process. If SIGTERM     signal is blocked, then the subsequent SIGKILL (9) terminates it.      -t timeout         Number of seconds to wait for command completion.         Default value: $DEFAULT_TIMEOUT seconds.      -i interval         Interval between checks if the process is still alive.         Positive integer, default value: $DEFAULT_INTERVAL seconds.      -d delay         Delay between posting the SIGTERM signal and destroying the         process by SIGKILL. Default value: $DEFAULT_DELAY seconds.  As of today, Bash does not support floating point arithmetic (sleep does), therefore all delay/time values must be integers. EOF }  # Options. while getopts ":t:i:d:" option; do     case "$option" in         t) timeout=$OPTARG ;;         i) interval=$OPTARG ;;         d) delay=$OPTARG ;;         *) printUsage; exit 1 ;;     esac done shift $((OPTIND - 1))  # $# should be at least 1 (the command to execute), however it may be strictly # greater than 1 if the command itself has options. if (($# == 0 || interval <= 0)); then     printUsage     exit 1 fi  # kill -0 pid   Exit code indicates if a signal may be sent to $pid process. (     ((t = timeout))      while ((t > 0)); do         sleep $interval         kill -0 $$ || exit 0         ((t -= interval))     done      # Be nice, post SIGTERM first.     # The 'exit 0' below will be executed if any preceeding command fails.     kill -s SIGTERM $$ && kill -0 $$ || exit 0     sleep $delay     kill -s SIGKILL $$ ) 2> /dev/null &  exec "$@" 
like image 45
Juliano Avatar answered Oct 02 '22 01:10

Juliano