Linux Basics: Clean Up Bash Temporary Files with Traps
Register cleanup just after creating temporary state. These examples remove only files they created, and use rmdir rather than recursive deletion.
These examples were tested on Linux with GNU Bash 5.2.37. Use Bash rather than sh. No packages or administrator access are needed.
Prepare a scratch directory
You need Bash, a text editor, and the mktemp, rm, and rmdir utilities. Keep using the same terminal. Save each named source file in this scratch directory before running its command. Calling bash explicitly avoids changing executable permissions.
start_dir="$PWD"
work_dir="$(mktemp -d)" || exit 1
cd -- "$work_dir" || exit 11. Remove one temporary file on exit
Save this as trap-file.sh:
#!/usr/bin/env bash
temp_file=$(mktemp) || exit 1
cleanup() {
if rm -- "$temp_file"; then
printf 'Temporary file removed\n'
else
printf 'Could not remove temporary file\n' >&2
fi
}
trap cleanup EXIT
printf '%s\n' 'temporary notes' > "$temp_file"
IFS= read -r line < "$temp_file"
printf 'Read: %s\n' "$line"Run it:
bash trap-file.shOutput:
Read: temporary notes
Temporary file removedThe EXIT trap runs when the script ends. A named function keeps cleanup readable. Variables are expanded when the function runs, so keep the temporary path unchanged.
2. Preserve a failure status during cleanup
Save this as trap-status.sh:
#!/usr/bin/env bash
temp_dir=$(mktemp -d) || exit 1
cleanup() {
status=$?
trap - EXIT
rmdir -- "$temp_dir"
printf 'Cleanup after status %d\n' "$status"
exit "$status"
}
trap cleanup EXIT
printf 'Stopping a simulated failed check\n'
exit 7Run it:
bash trap-status.sh
printf "Script status: %s\n" "$?"Output:
Stopping a simulated failed check
Cleanup after status 7
Script status: 7Capture the status before another command replaces it. Removing the EXIT trap before explicitly exiting avoids re-entry concerns. The failure here is deliberate; cleanup does not turn it into success.
3. Route a handled signal through EXIT cleanup
Save this as trap-signal.sh:
#!/usr/bin/env bash
temp_dir=$(mktemp -d) || exit 1
cleanup() {
if rm -f -- "$temp_dir/marker" && rmdir -- "$temp_dir"; then
printf 'Workspace removed\n'
else
printf 'Could not remove workspace completely\n' >&2
fi
}
trap cleanup EXIT
trap 'exit 143' TERM
printf '%s\n' ready > "$temp_dir/marker"
printf 'Sending TERM to this script only\n'
kill -TERM "$$"Run it:
bash trap-signal.sh
printf "Script status: %s\n" "$?"Output:
Sending TERM to this script only
Workspace removed
Script status: 143This script sends TERM only to itself. The handler explicitly exits with 143, then EXIT performs cleanup. Traps cannot handle SIGKILL or power loss, so temporary cleanup is best effort rather than a durability guarantee.
Clean up
In the same terminal, remove only the example files, return to your original directory, and remove the empty scratch directory:
rm -- trap-file.sh trap-status.sh trap-signal.sh
cd -- "$start_dir" || exit 1
rmdir -- "$work_dir"
unset start_dir work_dirReference
See the GNU Bash manual: Bourne Shell Builtins for the relevant shell behavior. The installed Bash help command also documents these builtins.
Acknowledgements
This post was created with assistance from AI.