68

Is there an easy way to let a specific command (only terminable through Ctrl-C) run for 5 minutes automatically?

For example:

minute-command 5-minutes ping www.google.com

or any other command that does not terminate itself.

I'd like to be able to specify a time limit, not only 5 minutes.

0

6 Answers 6

68

There are (at least) two programs that provide this functionality:

NAME

timelimit — effectively limit the absolute execution time of a process

SYNOPSIS

timelimit [-pq] [-S killsig] [-s warnsig] [-T killtime] [-t warntime] command [arguments ...]

and

NAME

timeout - run a command with a time limit

SYNOPSIS

timeout [OPTION] DURATION COMMAND [ARG]...
timeout [OPTION]

They are packaged as follows:

$ dlocate `which timeout timelimit`
timelimit: /usr/bin/timelimit
coreutils: /usr/bin/timeout

Comparison:

/-----------------------------+------------+----------------\
|            Feature          |  timelimit |     timeout    |
+=============================+============+================+
|   time to run               | -t time    | first argument |
+-----------------------------+------------+----------------+
|   terminate signal          | -s signal  | -s signal      |
+-----------------------------+------------+----------------+
|   grace period              | -T time    | -k time        |
+-----------------------------+------------+----------------+
|   kill signal               | -S signal  | (note 1)       |
+-----------------------------+------------+----------------+
|   propagate signals         | -p         | (note 2)       |
\-----------------------------+------------+----------------/

Notes:

  1. timeout always uses SIGKILL as its last-resort signal.
  2. timeout does not have any functionality to exit with a signal when the child program does so.

The exit status of the two programs differ, but that's hard to summarise neatly, so I suggest you consult the manual pages yourself for that.

As timeout is installed on more systems by default (coreutils is a standard package in many distributions), I suggest you use that unless you need the extra functionality provided by timelimit.

10
  • 10
    Why would you use one rather than the other? Feb 25, 2016 at 13:42
  • 2
    @BriceM.Dempsey On my local machine, timeout is present, but timelimit is not (although it is available from the repo). So, if one of them comes pre-installed... Apart from that, just looking at the method signatures I can see that they do different things, and have different use cases.
    – Benubird
    Feb 25, 2016 at 15:49
  • Note that there exist at least two implementations of timelimit. See How can I kill a process and be sure the PID hasn't been reused for more info on them and GNU timeout.
    – sch
    Feb 25, 2016 at 16:07
  • 1
    @BriceM.Dempsey timeout is included in GNU coreutils, timelimit is not. You can have installed none, one or both. For the second it is reported timelimit executes a command and terminates the spawned process after a given time with a given signal. A “warning” signal is sent first, then, after a timeout, a “kill” signal, similar to the way init(8) operates on shutdown. So even a different in the middle behaviour.
    – Hastur
    Feb 25, 2016 at 16:24
  • Since GNU coreutils is preinstalled on every GNU/Linux, timeout is the way to go there. Feb 26, 2016 at 9:17
87

Actually, this is what timeout is for:

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.

lx@lxtp:~$ dpkg -S /usr/bin/timeout
coreutils: /usr/bin/timeout
0
18

Pure bash built in, without coreutils

I found that this solution works in bash relying on a built-in command without calling an external executable. It works on system where eventually are not even been installed the coreutils [1]

YourCommand & read -t 300 ;  kill $!                           # 1st version
YourCommand & read -t 300 || kill $!                           # 2nd version 

Explanations: as usual when you send a command in the background with &, its PID is stored into the internal variable $! (present in the modern version of dash, csh, bash, tcsh, zsh...).
What really makes the difference among the shells is the presence of the built-in command read[2] and of the option -t. In the 1st version if the user will not complete a line of input before the specified amount of seconds the instruction will be terminated and an error return code will be generated.

-t TIMEOUT Cause read to time out and return failure if a complete line of input is not read within TIMEOUT seconds.

The second version works as the 1st but you can abort the killing timeout just pressing enter.
Indeed the or operator || executes the kill statement only if the read command exits with a return code different from zero, as when the timeout is expired. If you press enter before that moment, it will return 0 and it will not kill your previous command.


Coreutils solutions [1]

When coreutils are present on your system and you have no need to save the time and the resources to call an external program, timeout and sleep and are both perfect ways to reach your goal.

timeout The use of timeout is straightforward.
Eventually you can consider to use also the -k option to send an additional kill signal if the first fails.

timeout 5m YourCommand                                         # 3rd version 

sleep With sleep you can use your fantasy or take some inspirations[3]. Note that you can leave your command in background or in foreground (e.g. top usually needs to be in foreground).

YourCommand & sleep 5m; kill $!                                # 4th Background
YourCommand & pid=$! ; (sleep 5m; kill $pid;) &                # 5th Background

bash -c '(sleep 5m; kill $$) & exec YourCommand'               # 6th Foreground
(cmdpid=$BASHPID; (sleep 5m; kill $cmdpid) & exec YourCommand) # 7th Foreground

Explanations

  • In the 4th version you execute in background YourCommand then your shell sleeps for 5 minuites. When it will be finished the last background process ($!) will be killed. You stop your shell.
  • In the 5th version instead you execute in background YourCommand and you store immediately that PID in the variable $pid. Then you execute in background a nap of 5 minutes and its consequent command that will kill that stored PID. Since you sent this group of commands in background you do not stop your shell. You need to store the PID in a variable because the value of $! can be updated by an eventual execution of another program in background. In simple words you avoid the risk to kill the wrong process or no process at all.
  • In the 6th version it is called a new bash shell that will suicide itself in 5 minutes via $$, then it is executed your command that remains in foreground.
  • In the 7th version it is invoked a subshell () that stores its PID in a variable (cmdpid) and kills itself with another subshell sent in background execution, then run YourCommand in foreground.

Of course in each version you can send the kill signal you need, from the default one to the extreme kill -9, to be used only when really needed.

References

  • [1] The Coreutils
  • [2] The Bash Beginners Guide
  • [3] The BashFAQ
1
  • 2
    "With sleep you can use your fantasy or take some inspirations [link to Bash FAQ]" +1 #thatsenoughinternetfortoday Mar 3, 2016 at 16:12
11

For your particular case, most implementations of ping support -c or --count to terminate after a particular number of pings:

ping -c 300 host.example.com

For a more general solution, see the other answers.

1
  • 7
    I get the impression OP just provided ping for a concrete example. He doesn't want a case-by-case solution for every different program. Feb 25, 2016 at 16:23
8

You can do it with a simple script like this:

#!/bin/bash

#
# $1 is the time to let the program run, and $2, $3, ... are the command itself.
#

"${@:2}" &
PID=$!
sleep "${@:1:1}"
kill -2 $PID

(signal SIGINT=2 used as per Matija Nalis' suggestion in the comment below).

An explanation of the (uncommon?) bash expression $@:...: the positional parameters, ($*, $@, "$*", "$@") admit the following extra specification, for instance:

"${@:START:COUNT}"

which means: of all parameters, take COUNT of them, the first one to be taken being that at the STARTth position; if COUNT is omitted, take all of them till the end, beginning with the STARTth position. Remember that $0 is the program name. If START is negative, start counting from the end, and remember that COUNT cannot be negative, hence the last argument is "${@:-1}". Also, just about always include the positional parameters inside double quotes.

5
  • 1
    I'd probably design the script to take the time as the first or last arg, and run all the other args as the command. Letting bash word-split $1 is pretty gross. Also, you could just sleep "$1" or something in countdown. Feb 25, 2016 at 10:41
  • 1
    @PeterCordes Done, as per your wishes. Feb 25, 2016 at 10:58
  • The time in first arg lets you do time=$1; shift, and then you don't need the array-slicing syntax for the positional parameters. But yes, that's much simpler. Feb 25, 2016 at 11:16
  • 1
    @PeterCordes Agreed, but you asked me to see whether I knew a safer way to do this and I had just been lazy, or whether I was just plain ignorant. lol. Feb 25, 2016 at 11:24
  • as poster specifically asked about program that can be interrupted with ctrl-c, it is probably better to try kill -INT instead of kill -9 (or at least try -9 only after few seconds if the first one didn't suceed - it gives program chance to exit cleanly and not leave temporary files etc around) Feb 26, 2016 at 3:10
5

Since you mention ping in your example; this command has a few options to stop after a specific amount of time:

  • w deadline Specify a timeout, in seconds, before ping exits regardless of how many packets have been sent or received. In this case ping does not stop after count packet are sent, it waits either for deadline expire or until count probes are answered or for some error notification from network.
  • W timeout Time to wait for a response, in seconds. The option affects only timeout in absence of any responses, otherwise ping waits for two RTTs.

man ping

Not the answer you're looking for? Browse other questions tagged .