Linux Basics: Put a Deadline on Commands with GNU timeout
A scheduled check should not wait forever for a command that has stopped responding. GNU timeout starts another program with a deadline. These small experiments distinguish a successful run, an expired deadline, and a program that reports its own failure.
These examples were tested on Linux with GNU Bash 5.2.37 and GNU coreutils 9.7. Use Bash rather than sh. The commands operate only on the small practice inputs shown here.
Prepare a scratch directory
You need GNU timeout, sleep, Bash, a text editor, and the mktemp, rm, and rmdir utilities. Keep using the same terminal. Save each named source file in this new directory, then run it once in the order shown. Invoking bash explicitly avoids changing executable permissions. No administrator access is needed.
start_dir="$PWD"
work_dir="$(mktemp -d)" || exit 1
cd -- "$work_dir" || exit 11. Let a quick command finish
Save this as timeout-finish.sh:
#!/usr/bin/env bash
set -eu
timeout 2s bash -c 'printf "Report ready\n"'
printf 'Status: %s\n' "$?"Run it:
bash timeout-finish.shOutput:
Report ready
Status: 0The child Bash prints one line and exits successfully before the two-second deadline. The following printf observes status zero. A time limit is a maximum wait, not a delay that every successful command must consume.
Here bash -c deliberately starts a shell for the small command string. A bare shell builtin or a complete pipeline is not automatically executed by timeout as shell syntax.
2. Recognize an expired deadline
Save this as timeout-deadline.sh:
#!/usr/bin/env bash
set -eu
if timeout 0.2s sleep 2; then
printf 'Unexpected early finish\n'
exit 1
else
status=$?
fi
printf 'Status: %s\n' "$status"
[ "$status" -eq 124 ] || exit 1
printf 'Deadline reached\n'Run it:
bash timeout-deadline.shOutput:
Status: 124
Deadline reachedThe two-second sleep is interrupted after the shorter deadline. The if statement gives the expected nonzero result an explicit handling path, even with set -e enabled. Save $? immediately: another command would replace it.
The script verifies 124 before announcing the timeout. This checks the reason instead of treating every failure as an expired deadline. The fractional duration keeps the exercise short; it is not a real-time scheduling guarantee.
3. Keep a child failure distinct
Save this as timeout-child-status.sh:
#!/usr/bin/env bash
set -eu
if timeout 2s bash -c 'exit 7'; then
printf 'Unexpected success\n'
exit 1
else
status=$?
fi
printf 'Status: %s\n' "$status"
[ "$status" -eq 7 ] || exit 1
printf 'The command failed before the deadline\n'Run it:
bash timeout-child-status.shOutput:
Status: 7
The command failed before the deadlineThe child intentionally returns 7 without waiting. The wrapper preserves that result, and the script checks it before printing its explanation. In an actual check, this path can represent an application error rather than a slow application.
A child can itself return one of the codes used by timeout, including 124. Know the child program’s exit-code contract if your automation must distinguish every possible outcome.
Keep in mind
GNU timeout normally sends TERM when the deadline expires. A program can catch or ignore that signal. The optional --kill-after=2s sends KILL two seconds after the initial signal if needed, but forced termination prevents normal cleanup. Do not use a deadline as a substitute for transactional writes or backups.
A duration of zero disables its associated timeout. --preserve-status changes how an expired run is reported, so do not add it while still relying on 124. Other wrapper failures include 125 for an internal failure, 126 for a command that cannot be invoked, and 127 for a missing command. Consult the manual when designing a complete failure policy.
Clean up
In the same terminal, remove only the named practice files, return to your original directory, and remove the now-empty scratch directory:
rm -- timeout-finish.sh timeout-deadline.sh timeout-child-status.sh
cd -- "$start_dir" || exit 1
rmdir -- "$work_dir"
unset start_dir work_dirReferences
Acknowledgements
This post was created with assistance from AI.