Running Commands with Time Limit in Bash
This post shows how to run a command with a time limit in Bash, considering several shell options.
tl;dr
See this code snipet.
The timeout command
For this purpose, we use GNU’s timeout
command.
- timeout(1) - Linux manual page
- Usage:
timeout [OPTION] DURATION COMMAND [ARG]...
The manual page says:
If the command times out, and –preserve-status is not set, then exit with status 124.
This can be problematic when you have enabled the errexit
option in Bash, and you want to continue your script after the command has timed out.
Shell options in Bash
Bash has a variety of built-in shell options.
- The Set Builtin - Bash Manual
- Usage:
- to enable an option:
set [--abefhkmnptuvxBCEHPT] [-o option-name] [argument …]
- to disable an option:
set [+abefhkmnptuvxBCEHPT] [+o option-name] [argument …]
- to enable an option:
Here is a list of the options.
Name | Short | Default | Description |
---|---|---|---|
allexport | a | Off | Each variable or function that is created or modified is given the export attribute and marked for export to the environment of subsequent commands. |
braceexpand | B | On | The shell will perform brace expansion. |
emacs | On | Use an emacs-style line editing interface. | |
errexit | e | Off | Exit immediately if a pipeline returns a non-zero status. |
errtrace | E | Off | If set, any trap on ERR is inherited by shell functions, command substitutions, and commands executed in a subshell environment. |
functrace | T | Off | If set, any trap on DEBUG and RETURN are inherited by shell functions, command substitutions, and commands executed in a subshell environment. |
hashall | h | On | Locate and remember (hash) commands as they are looked up for execution. |
histexpand | H | On | Enable ! style history substitution. |
history | On (*) | Enable command history. *This option is on by default in interactive shells. | |
ignoreeof | Off | An interactive shell will not exit upon reading EOF. | |
keyword | k | Off | All arguments in the form of assignment statements are placed in the environment for a command, not just those that precede the command name. |
monitor | m | On | Job control is enabled. All processes run in a separate process group. When a background job completes, the shell prints a line containing its exit status. |
noclobber | C | Off | Prevent output redirection using > , >& , and <> from overwriting existing files. |
noexec | n | Off(*) | Read commands but do not execute them. This may be used to check a script for syntax errors. *This option is ignored by interactive shells. |
noglob | f | Off | Disable filename expansion (globbing). |
nolog | Off | Currently ignored. | |
notify | b | Off | Cause the status of terminated background jobs to be reported immediately, rather than before printing the next primary prompt. |
nounset | u | Off | Treat unset variables and parameters other than the special parameters @ or * as an error when performing parameter expansion. An error message will be written to the standard error, and a non-interactive shell will exit. |
onecmd | t | Off | Exit after reading and executing one command. |
physical | P | Off | If set, do not resolve symbolic links when performing commands such as cd which change the current directory. The physical directory is used instead. By default, Bash follows the logical chain of directories when performing commands which change the current directory. |
pipefail | Off | If set, the return value of a pipeline is the value of the last (rightmost) command to exit with a non-zero status, or zero if all commands in the pipeline exit successfully. | |
posix | Off | Change the behavior of Bash where the default operation differs from the POSIX standard to match the standard. | |
privileged | p | Off | Turn on privileged mode. |
verbose | v | Off | Print shell input lines as they are read. |
vi | Off | Use a vi-style line editing interface. | |
xtrace | x | Off | Print a trace of simple commands, for commands, case commands, select commands, and arithmetic for commands and their arguments or associated word lists after they are expanded and before they are executed. |
For example, you can enable the errexit
option by either set -e
or set -o errexit
, and you can disable that option by either set +e
or set +o errexit
. The shell variable $-
represents the currently enabled options.
bash-3.2$ echo $-
himBH
bash-3.2$ set -e
bash-3.2$ echo $-
ehimBH
bash-3.2$ set +e
bash-3.2$ echo $-
himBH
bash-3.2$ set -o errexit
bash-3.2$ echo $-
ehimBH
bash-3.2$ set +o errexit
bash-3.2$ echo $-
himBH
Wrapping the timeout command
So, if the errexit
option is set, we want to temporarily disable it and then run the timeout
command.
Here is an example wrapper for such purposes.
TIMEOUT=timeout
run_with_timeout() {
oldopt=$-
set +e
$TIMEOUT $@
ret=$?
if [[ $ret -eq 124 ]]; then
ret=0
fi
set -$oldopt
return $ret
}
Note that we overwrite the return code with 0
if the command times out (in that case, timeout
returns status code 124
). Also, we back up the current shell options and restore them before exiting the function.
We can test this function with the following code.
set -e
run_with_timeout 1s true
echo "Success within time limit: rc=$?"
run_with_timeout 1s sleep 10
echo "Timeout: rc=$?"
run_with_timeout 1s false
echo "*THIS SHOULD NOT BE PRINTED* Failure within time limit: rc=$?"
The output should be like this:
Success within time limit: rc=0
Timeout: rc=0