Problem
When a trap ... EXIT is set inside a command substitution $(...), the trap handler fires but its output escapes to the parent's stdout instead of being captured by the $(...). Explicit subshells (...) work correctly.
Reproduction
# Command substitution — BROKEN (trap output escapes):
out="$(trap 'echo TRAP' EXIT; echo BODY)"
echo "captured: [${out}]"
# Expected: captured: [BODY\nTRAP]
# Actual: captured: [BODY]
# TRAP ← appears on parent stdout, not captured
# Explicit subshell — WORKS:
(trap 'echo SUB_TRAP' EXIT; echo SUB_BODY)
# Output: SUB_BODY
# SUB_TRAP ✓
Impact
Low for harness specifically (it uses EXIT traps for temp file cleanup in hook scripts, not in $(...)), but it's a correctness issue.
Test cases
# EXIT trap output should be captured by $()
result="$(trap 'echo TRAPPED' EXIT; echo hello)"
echo "${result}"
# Expected stdout:
# hello
# TRAPPED
# Trap with explicit exit
result2="$(trap 'echo CLEANUP' EXIT; echo data; exit 0)"
echo "${result2}"
# Expected stdout:
# data
# CLEANUP
# Trap output should not leak to parent stdout
out="$(trap 'echo INSIDE' EXIT; echo body)"
# The word "INSIDE" should NOT appear outside of $out
# Expected: only "body\nINSIDE" in $out
# Nested: $() inside $()
outer="$(
inner="$(trap 'echo inner-trap' EXIT; echo inner-body)"
echo "inner=${inner}"
trap 'echo outer-trap' EXIT
echo "outer-body"
)"
echo "${outer}"
# Expected stdout:
# inner=inner-body
# inner-trap
# outer-body
# outer-trap
Problem
When a
trap ... EXITis set inside a command substitution$(...), the trap handler fires but its output escapes to the parent's stdout instead of being captured by the$(...). Explicit subshells(...)work correctly.Reproduction
Impact
Low for harness specifically (it uses EXIT traps for temp file cleanup in hook scripts, not in
$(...)), but it's a correctness issue.Test cases