What is a simple way to let a command run for 5 minutes?
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
There are (at least) two programs that provide this functionality:
NAME
timelimit
— effectively limit the absolute execution time of a processSYNOPSIS
timelimit [-pq] [-S killsig] [-s warnsig] [-T killtime] [-t warntime] command [arguments ...]
and
NAME
timeout
- run a command with a time limitSYNOPSIS
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:
timeout
always usesSIGKILL
as its last-resort signal.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
.
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 shellsleep
s 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