Course outline

Bash / Intermediate: reliable data and failure paths / Lesson 9

Use traps for bounded lifecycle handling

Preserve status and cleanup invariants while recognizing signal timing, trap inheritance, and interruption limits.

About 4 min readContent reviewed
On this page

Lesson goals

  • Separate EXIT cleanup from ERR diagnostics.
  • Preserve the triggering status through cleanup.
  • Design idempotent partial-resource cleanup without universal guarantees.

A trap is shell code run at a lifecycle event

trap registers an action for selected signals or shell-defined events such as EXIT. The action is parsed and executed later, so quoting determines whether values are expanded during registration or during execution. Prefer a named cleanup function with clear state rather than a long interpolated command string. EXIT supports ordinary shell termination paths; it is not a guarantee against power failure or SIGKILL. Keep durable data safe independently of trap execution. A trap should complement an explicit operation contract, not become the only place where success or failure is decided.

Capture the incoming status before cleanup commands

The cleanup path can itself run commands that replace $?. Capture the incoming status immediately and decide whether cleanup failure should change the reported result. Often the original operation failure should remain primary while cleanup trouble is reported separately. A cleanup function should tolerate resources that were never created or were already released. Register cleanup after the relevant ownership state is established, and quote every owned path. This lesson uses only synthetic state and private temporary directories; it never removes arbitrary paths or relies on a trap to authorize a destructive operation.

bash Observe the original status through EXIT handling
bash -c 'cleanup() { status=$?; printf "exit-status=%s\n" "$status"; }; trap cleanup EXIT; exit 7'
child_status=$?
printf 'observed=%s\n' "$child_status"
Expected output
exit-status=7
observed=7

A disposable child exits seven and its EXIT handler prints the saved value. The parent observes seven and ends successfully after reporting the controlled experiment.

Signal delivery and shell waiting affect timing

A signal trap does not mean the action interrupts every external command at an arbitrary instant. Bash has documented behavior while waiting for foreground commands and while using wait for asynchronous work. A trapped signal can change the wait result, and child coordination still needs an explicit policy. SIGKILL and SIGSTOP cannot be handled by shell traps. Cancellation should identify owned children, request the appropriate documented action, allow bounded completion, and preserve status. Never target guessed PIDs or broad process names; advanced job lessons restrict demonstrations to children created by the example.

ERR follows exclusions and inheritance rules

ERR is a Bash event with conditions related to errexit exclusions. A failure tested by if, negated with !, or placed in certain lists and pipelines may not trigger the assumed diagnostic path. Function and substitution inheritance depend on options such as errtrace and context. Therefore an ERR handler is useful supporting observability, not a universal exception system. Keep explicit handling around required operations and test the actual caller structure. Traces and diagnostics can expose secrets, so avoid dumping all variables or expanded commands merely because a failure occurred.

Test partial progress and repeated cleanup

A lifecycle design should survive failure before allocation, after allocation, and after successful publication. Track which resources the script actually owns and separate temporary state from the only copy of important content. Idempotent cleanup means a repeated request does not create another error or affect unrelated state. Prefer private temporary names from the platform’s supported tool rather than predictable shared filenames. Verify success and failure paths with harmless markers before introducing real resources. The example below demonstrates a repeatable state transition only; deleting real data requires an independent reviewed operation design.

bash Make a cleanup action idempotent in synthetic state
released=0
cleanup() {
  if (( released == 0 )); then
    released=1
    printf 'released\n'
  fi
}
cleanup
cleanup
printf 'state=%s\n' "$released"
Expected output
released
state=1

The second call has no additional effect. This verifies the ownership-state pattern without deleting files or signaling another process.

Common errors and fixes

Cleanup reports the logger’s status

Why it happens
The original $? was overwritten first.
How to fix it
Save the incoming status before any cleanup command.
Prevent it next time
Test a controlled nonzero exit through the handler.

A trap is treated as crash-proof storage protection

Why it happens
Uncatchable signals and machine failure were ignored.
How to fix it
Preserve independent durable copies and publication rules.
Prevent it next time
Document what cleanup cannot guarantee.

ERR is expected for every nonzero command

Why it happens
Grammar and inheritance exclusions were not considered.
How to fix it
Handle required work explicitly and test call contexts.
Prevent it next time
Use ERR as supporting diagnostics only.

Check your understanding

1. What must cleanup do if allocation failed before ownership was recorded?

Show worked solution

Recognize that no owned resource exists and return without affecting unrelated state. Cleanup should use explicit initialization and ownership flags rather than guess a default path. Test this path independently from successful allocation.

2. Why can a tested failing command skip an expected ERR message?

Show worked solution

ERR obeys documented control-flow exclusions. The failure is being consumed by a condition or another listed context. Put the required failure decision in that branch and treat trap output as supplementary evidence, not the sole error mechanism.

Key takeaways

  • Preserve status and ownership before cleanup work.
  • Test traps within their actual signal and call context.

Official references