Put a Hard Time Limit Around a Linux Command with timeout
You will run a command with a deadline, tell a genuine timeout apart from an ordinary command failure, and give a slow process a short chance to clean up before forcing it down. The examples use GNU timeout from coreutils 9.4, installed here as package version 9.4-3ubuntu6.3.
The route
Jump straight to the step you need, or tick off Done means at the end.
Allow about ten minutes. You need a shell and a command you can run safely. The examples are ordinary, unprivileged commands. Do not put sudo inside a timeout merely because a process is slow: privilege changes the impact of the command, not the timeout semantics.
1. Check the installed command
Confirm which executable is in your path and read the local version. This changes nothing:
$ command -v timeout
/usr/bin/timeout
$ timeout --version | head -1
timeout (GNU coreutils) 9.4
The command shape is timeout DURATION COMMAND ARGUMENTS. A duration is a floating-point number followed by s, m, h or d. Seconds are the default, so 2 means two seconds, but 2s is easier to review in a script.
Checkpoint: make sure the command after the duration is the program you actually intend to run. Everything after that program belongs to it.
2. Stop a command after its deadline
Use a harmless sleep to see the basic result:
$ timeout 2s sh -c 'sleep 5'
$ status=$?
$ printf 'exit status: %s\n' "$status"
exit status: 124
After two seconds, timeout sends TERM, the default signal, to the command. Status 124 means the command timed out when the normal status policy is in use. The shell prompt should return well before the five-second sleep completes.
This is a process action, not a dry run. A timeout can interrupt a database migration, file transfer or deployment halfway through. Test the wrapped command on representative data before putting the pattern into automation.
3. Interpret the exit status without guessing
The default status table is small but easy to misread:
| Status | Meaning |
|---|---|
124 | The command timed out, unless --preserve-status was used. |
125 | timeout itself failed. |
126 | The command was found but could not be invoked. |
127 | The command could not be found. |
137 | The command, or timeout itself, was killed by signal 9. |
| other | The wrapped command's status, when it finishes normally. |
Do not treat every non-zero value as a timeout. For example, this command finishes normally with status 7:
$ timeout 10s sh -c 'exit 7'
$ printf 'exit status: %s\n' "$?"
exit status: 7
Capture $? immediately. Running printf, echo or another check first replaces the status you meant to inspect.
4. Give the process time to clean up
Some programs catch TERM to flush data or remove a temporary file. Add -k when you want a second deadline for a forced kill:
$ timeout -k 1s 2s sh -c 'trap "" TERM; sleep 10'
$ status=$?
$ printf 'exit status: %s\n' "$status"
exit status: 137
The first duration is the main limit. The -k 1s duration starts after the initial signal, so this process receives KILL roughly one second later because it ignores TERM. KILL cannot be caught or deferred. Use the smallest grace period that lets the application perform its documented cleanup.
Warning
A forced kill can leave locks, partial files or an interrupted transaction. For stateful work, prefer an application-level cancellation mechanism when one exists, and make the operation restartable.
5. Choose a different initial signal
Use -s or --signal when the program has a deliberate signal contract:
$ timeout --signal=INT 2s sh -c 'trap "printf \"interrupt received\\n\"" INT; sleep 10'
interrupt received
The signal may be a name such as HUP or INT, or a number. Check names on the local system with kill -l. Do not change the signal just to hide a failing command: TERM is the normal polite request, while the correct alternative depends on the program.
6. Decide whether to preserve the command status
By default, a timeout masks the command's final status with 124. --preserve-status keeps the command's status instead, including a status produced after the command handles the initial signal:
$ timeout --preserve-status 1s sh -c 'trap "" TERM; sleep 2; exit 7'
$ printf 'exit status: %s\n' "$?"
exit status: 7
This option is useful when the wrapped program owns a meaningful status protocol, but it makes timeout detection less obvious. If a caller must distinguish a deadline from application failure, keep the default or add a separate record of the deadline event.
A duration of 0 disables the associated timeout. That means timeout 0 command does not impose a deadline, and timeout -k 0s 2s command does not add a forced-kill grace period. Treat zero as a deliberate configuration value, not as an accidental substitute for a missing variable.
7. Handle interactive and wrapper cases carefully
When timeout is run directly from a shell prompt, the wrapped command normally does not need --foreground. That option is for cases where timeout is not running directly from a prompt and the command must read from the terminal and receive terminal signals. In foreground mode, children of the command are not timed out.
That last detail matters for wrappers. If a script launches a worker, a foreground timeout may end while the worker remains alive. Check the program's process model before relying on the deadline. For a batch job, make the worker lifecycle explicit and verify that no child remains after a test run.
Use --verbose when diagnosing a timeout. It reports signals sent to standard error; it does not make the deadline stronger and does not repair a command that ignores signals.
Done means
- You confirmed the local GNU coreutils version and command path.
- The duration and command boundary are explicit, with arguments quoted where needed.
- Your caller distinguishes status 124 from ordinary command failures.
- A slow process gets a tested cleanup window before any
KILL. - You use
--preserve-statusonly when its loss of simple timeout detection is intentional. - You tested the wrapped process and its children without risking production data.