subshell
BashINFOCommonScriptingHIGH confidence

Subshell or command substitution exit code

Production Risk

Very common silent bug in bash scripts; always test failure cases.

What this means

Subshells (parentheses `()`) and command substitutions (`$()`) have their own exit codes that propagate to the parent. A common bug is capturing command output with `$()` and losing the exit code by assigning it to a variable on the same line.

Why it happens
  1. 1Assigning `var=$(command)` on one line — the assignment always succeeds (exit 0), losing the command's exit code
  2. 2set -e not triggering inside `$()`
  3. 3Expecting a subshell failure to propagate when it is in a condition
How to reproduce

Losing a command exit code by assigning in the same statement.

trigger — this will error
trigger — this will error
#!/bin/bash
set -e

# BUG: assignment always exits 0 — set -e won't trigger
output=$(false)  # false exits 1, but assignment exits 0
echo "Reached here with exit: $?"  # prints 0!

expected output

Reached here with exit: 0

Fix

Separate assignment from exit code capture

WHEN Assigning command output AND checking exit code

Separate assignment from exit code capture
#!/bin/bash
set -e

# Correct: separate assignment and check
output=$(some_command) || { echo "some_command failed" >&2; exit 1; }

# Or assign then check $? separately:
output=$(some_command)
# With set -e, this line triggers if some_command failed

Why this works

Bash evaluates `var=$(cmd)` as an assignment, which always exits 0. Use `|| {}` to catch the failure inline.

What not to do

Assume set -e catches failures inside $() assignments

The assignment statement wraps the exit code; set -e does not see the inner failure.

Content generated with AI assistance and reviewed for accuracy. Found an error? hello@errcodes.dev

← All Bash errors