wait / trap
| Since: | All Linux | |
|---|---|---|
| macOS(2001 Cheetah) | ||
| Bash 1.0(1989) |
wait waits for background processes to finish. trap catches signals and shell exit events to run custom code. Both are essential for implementing cleanup logic and safe exit handling in scripts.
Syntax
Use wait to wait for processes.
wait # Wait for all background jobs to finish wait $PID # Wait for the specified PID to finish wait %job_num # Wait for the specified job to finish
Use trap to catch signals.
trap 'command' signal_name... trap 'command' EXIT # Always runs when the script exits trap 'command' INT # Catches Ctrl+C (SIGINT) trap 'command' TERM # Catches SIGTERM trap 'command' ERR # Catches non-zero exit status from any command
Remove a trap.
trap - signal_name # Restore default behavior trap '' signal_name # Ignore the signal
Command List
| Command / Signal | Description |
|---|---|
| wait | Waits until all background jobs have completed. |
| wait $PID | Waits until the process with the given PID exits, then returns its exit status. |
| trap 'command' EXIT | Runs when the script exits, whether normally or due to an error. Useful for deleting temporary files. |
| trap 'command' INT | Runs when the user presses Ctrl+C. |
| trap 'command' TERM | Runs when the script receives SIGTERM. |
| trap 'command' ERR | Runs when a command returns a non-zero exit status. |
| trap 'command' HUP | Runs when SIGHUP is received (terminal disconnect or config reload). |
| trap '' INT | Ignores Ctrl+C, preventing accidental interruption during critical operations. |
Sample Code
Use trap ... EXIT to reliably delete a temporary file. The cleanup runs regardless of whether the script exits normally, with an error, or via Ctrl+C.
sample_cleanup.sh
#!/bin/bash tmpfile=$(mktemp /tmp/myapp.XXXXXX) echo "Temp file: $tmpfile" trap 'rm -f "$tmpfile"; echo "Cleanup complete"' EXIT echo "Processing data" > "$tmpfile" cat "$tmpfile"
Run the following command:
bash cleanup.sh Temp file: /tmp/myapp.aB3xYz Processing data Cleanup complete
This example catches Ctrl+C (SIGINT) and SIGTERM for a clean shutdown. Putting the cleanup logic in a function makes it easier to reuse across multiple signals.
sample_safe_exit.sh
#!/bin/bash
cleanup() {
echo ""
echo "Interrupt detected. Exiting safely..."
exit 0
}
trap cleanup INT TERM
echo "Press Ctrl+C to exit (auto-exits in 3 seconds)"
sleep 3
echo "Normal exit"
Run the following command:
bash safe_exit.sh Press Ctrl+C to exit (auto-exits in 3 seconds) Normal exit
The following is an advanced example. It combines arrays, background execution, and the RANDOM variable together.
This example combines wait and trap for parallel processing. Multiple workers run in the background, and their exit statuses are checked individually.
sample_parallel_worker.sh
#!/bin/bash
worker() {
local id=$1
sleep $(( RANDOM % 3 + 1 ))
echo "Worker $id done"
return 0
}
pids=()
for i in {1..3}; do
worker $i &
pids+=($!)
done
echo "Waiting for all workers to finish..."
for pid in "${pids[@]}"; do
wait "$pid"
echo "PID $pid exit status: $?"
done
echo "All workers done"
Run the following command:
bash parallel_worker.sh Waiting for all workers to finish... Worker 2 done Worker 3 done Worker 1 done PID 12345 exit status: 0 PID 12346 exit status: 0 PID 12347 exit status: 0 All workers done
Common Mistakes
Common Mistake 1: Placing trap after the code it should protect
trap only catches signals and exits that occur after the trap is registered. Any code that runs before the trap line is unprotected. Always declare trap at the top of the script.
sample_trap_late.sh
#!/bin/bash tmp=$(mktemp) echo "working..." > "$tmp" trap 'rm -f "$tmp"; echo "cleaned up"' EXIT
If the script is interrupted before reaching the trap line, the temporary file is not cleaned up. Move the trap to immediately after mktemp.
sample_trap_early.sh
#!/bin/bash tmp=$(mktemp) trap 'rm -f "$tmp"; echo "cleaned up"' EXIT echo "working..." > "$tmp"
Common Mistake 2: Checking exit status without wait — the status is always 0
Without wait, the background process has not finished yet when you check $?, so it reflects the exit status of the last foreground command, not the background job.
sample_wait_mistake.sh
#!/bin/bash false & echo "exit status: $?"
Run the following command:
bash wait_mistake.sh exit status: 0 ($? reflects echo's status, not false's)
Use wait $pid to capture the background job's exit status correctly.
sample_wait_correct.sh
#!/bin/bash false & pid=$! wait "$pid" echo "exit status: $?"
Run the following command:
bash wait_correct.sh exit status: 1
Notes
trap ... EXIT runs whenever the script exits — on normal exit, error exit, SIGTERM, or SIGINT — but not on SIGKILL. It is the standard pattern for cleanup tasks such as deleting temporary files, releasing lock files, and writing to logs. It is recommended practice to include this in every production script.
Defining the trap handler as a function lets you register the same logic for multiple signals and update it later.
For more on background execution, see & (background execution).
If you find any errors or copyright issues, please contact us.