Processes and jobs
timeout
Run a command with a time limit, and kill it if it is still running when the time is up.
timeout [OPTION...] DURATION COMMAND [ARG...]
timeout 10 cmd runs cmd and, if it has not finished after 10 seconds, sends it TERM. It
is the simplest way to stop a script from hanging forever on a network call, a lock or a test
that never returns. Durations take a suffix: s (the default), m, h or d, and fractions
are allowed (0.5, 1.5m).
The exit status tells you what happened. If the command finished in time, timeout returns the
command's own status. If it was killed, timeout returns 124. 125 means timeout itself
failed, 126 the command could not be run, 127 it was not found. With -s KILL the status is
137 instead of 124 (128 + 9). Checking for 124 is how a script tells "too slow" from "failed".
Programs that catch or ignore TERM can outlive the limit; -k DURATION sends KILL that long
after the first signal, which nothing can ignore. When the command is a shell script with
children, timeout signals the whole group it started, so the children are stopped too.
Examples
Limit how long a command may run
timeout 1 sleep 5
echo "exit status: $?"
exit status: 124
sleep 5 is killed after one second. The exit status 124 is timeout's way of saying "the
time ran out", distinct from any status the command itself returns.
A command that finishes in time keeps its own status
timeout 5 ls README.md missing.txt
echo "exit status: $?"
ls: cannot access 'missing.txt': No such file or directory README.md exit status: 2
ls finished well within the limit, so its own exit status (2, a file was missing) passes
straight through.
Tell a timeout from a failure in a script
timeout 2 curl -s http://localhost:8080/api/status.json > status.json
rc=$?
if [ "$rc" -eq 124 ]; then echo "timed out"
elif [ "$rc" -ne 0 ]; then echo "failed with $rc"
else echo "ok: $(wc -c < status.json) bytes"; fi
ok: 56 bytes
Save $? right away, then branch on it: 124 means slow, anything else non-zero means the
command itself failed. Here the local test server answered at once.
Use minutes, hours and fractions
timeout 0.5 sleep 10; echo "0.5 s: $?"
timeout 1m sleep 0.1; echo "1 minute: $?"
0.5 s: 124 1 minute: 0
0.5 is half a second and 1m a minute (h and d work too). The second sleep ends long
before its limit, so the status is sleep's own 0.
Report when the limit is hit
timeout -v 1 sleep 5
echo "exit status: $?"
timeout: sending signal TERM to command ‘sleep’ exit status: 124
-v (--verbose) prints a line on standard error naming the signal sent, which makes
timeouts visible in CI and cron logs.
Kill a command that ignores TERM
timeout -k 1 1 bash -c 'trap "" TERM; while :; do sleep 0.1; done'
echo "exit status: $?"
/bin/bash: line 1: 3422 Killed timeout -k 1 1 bash -c 'trap "" TERM; while :; do sleep 0.1; done' exit status: 137
The child ignores TERM (and so does every sleep it starts), so the first signal does
nothing. -k 1 sends KILL one second later. timeout delivers it to the whole process
group it runs in, itself included, so bash reports timeout as Killed and the status is
137 (128 + 9) rather than 124.
Send a different signal
timeout -s INT 1 bash -c 'trap "echo caught INT, saving work; exit 3" INT; sleep 5 & wait'
echo "exit status: $?"
caught INT, saving work exit status: 124
-s INT sends what Ctrl-C would, which some programs handle more gracefully than TERM.
The child cleans up and exits 3, but because the time ran out, timeout still reports 124.
Return the command's status even on timeout
timeout --preserve-status 1 sleep 5
echo "exit status: $?"
exit status: 143
--preserve-status returns whatever the command exited with, here 143 (killed by TERM),
instead of 124. Useful when a caller only understands the command's own codes.
Give a command a deadline for reading input
sleep 2 | timeout 0.5 cat
echo "exit status: ${PIPESTATUS[1]}"
exit status: 124
cat waits for input that is not coming. timeout gives up after half a second; PIPESTATUS[1]
holds the status of the second command of the pipeline. For prompts in your own scripts,
read -t is simpler.
Exit codes for a command that cannot run
timeout 5 no-such-command
echo "not found: $?"
timeout 5 ./README.md
echo "not executable: $?"
timeout 5x sleep 1
echo "bad duration: $?"
timeout: failed to run command ‘no-such-command’: No such file or directory not found: 127 timeout: failed to run command ‘./README.md’: Permission denied not executable: 126 timeout: invalid time interval ‘5x’ Try 'timeout --help' for more information. bad duration: 125
127 means the command does not exist, 126 that it exists but cannot be executed, and 125
that timeout itself rejected its arguments, here the unknown unit x.
Time-limit a shell function
slow() { sleep 5; echo "never"; }
export -f slow
timeout 1 bash -c slow
echo "exit status: $?"
exit status: 124
timeout runs programs, not shell functions. Export the function with export -f and run
it in a child bash, which timeout can then limit.
Common pitfalls
Treating every non-zero status as "timed out". Only 124 (or 137 with
-s KILL) means the limit was hit; other codes come from the command.Putting options after the duration:
timeout 10 -s KILL cmdruns a program called-s. Options go first:timeout -s KILL 10 cmd.Using
timeouton something interactive from a script.timeoutputs the command in the background from the terminal's point of view, so it cannot read the keyboard;--foregroundallows it, at the cost of not timing out the command's children.Assuming
TERMis enough. A process that ignores it keeps running; pair the limit with-k(timeout -k 5 60 cmd) when it must end.Wrapping a function, alias or builtin:
timeout 5 myfuncfails with status 127. Runbash -caround it.
GNU vs BSD and macOS
The examples above ran the GNU/Linux version. These are the differences that matter on macOS and the BSDs (written from their documentation, not run here).
timeoutis from GNU coreutils. macOS does not ship it; Homebrew's coreutils installs it asgtimeout. FreeBSD has its owntimeoutwith the same basic usage and the 124 convention.
Options worth knowing
A selection, described in our own words. For the full list, see timeout(1) or timeout --help.
| Option | What it does |
|---|---|
-s, --signal SIGNAL | Send SIGNAL (a name such as INT or a number) on timeout instead of TERM. |
-k, --kill-after DURATION | If the command is still running DURATION after the first signal, send KILL. |
-p, --preserve-status | Exit with the command's status even when it timed out. |
-v, --verbose | Print a message on standard error when a signal is sent. |
-f, --foreground | Let the command use the terminal; its children are then not timed out. |
Related
killSend a signal to processes by PID or job number; by default TERM, which asks a process to exit.sleepPause for a given amount of time, in seconds by default, with m, h and d suffixes and fractions allowed.waitWait for background jobs of the current shell to finish, and return their exit status.timeMeasure how long a command takes (wall-clock, user and system CPU time); GNU /usr/bin/time also reports memory and I/O.nohupRun a command immune to the hangup signal, so it keeps running after you log out or close the terminal.- Exit codesWhat exit statuses mean, how $? works, the reserved values 126, 127 and 128+N, PIPESTATUS, and how to choose codes for your own scripts.