Skip to content
Command Substitution
step 1/7

Reading — step 1 of 7

Learn

~2 min readCommand Substitution and Exit Status

Command substitution captures the OUTPUT of a command into a variable or argument. The Bash Reference Manual treats it as foundational — it's how shell scripts compose programs.

Two syntaxes

# Modern preferred:
date_str=$(date +%Y-%m-%d)
echo "Today is $date_str"

# Legacy backtick form (avoid):
date_str=`date +%Y-%m-%d`

$(...) is preferred because:

  • Easier to read
  • Easier to nest: $(echo $(date))
  • Backticks need escaping inside backticks
  • Tools / linters expect $(...)

Capturing output

file_count=$(ls /etc | wc -l)
echo "Found $file_count files"

user=$(whoami)
echo "Running as $user"

# Multiple words:
files=$(ls *.txt)
for f in $files; do                # WORD-SPLIT — fragile
    echo "$f"
done

Quoting matters

# UNQUOTED — word-splits and globs the result:
for f in $(ls *.txt); do echo "$f"; done
# Breaks if filenames have spaces, *, ?, or newlines

# QUOTED — preserves whole output as single string:
output="$(ls *.txt)"
echo "$output"

# For loops over files: use globs, not ls:
for f in *.txt; do echo "$f"; done

The for f in $(ls) pattern is a common trap — bash splits on IFS (whitespace) and globs. Use globs directly instead.

Trailing newlines

Command substitution STRIPS trailing newlines:

echo "hello

" | wc -c           # 8 (hello + 2 newlines = 7 bytes, + echo's own trailing newline)
result=$(echo "hello

")
echo ${#result}                       # 5 ("hello" — newlines stripped)

Useful 99% of the time; surprises if you wanted to preserve trailing whitespace.

Capturing both stdout and stderr

# Combine stderr into stdout:
output=$(some_command 2>&1)

# Capture stderr separately (bash 4+):
output=$(some_command 2> /tmp/stderr.log)
err=$(<"/tmp/stderr.log")

Process substitution — different beast

# diff two command outputs without temp files:
diff <(ls /etc) <(ls /tmp)

# `<(cmd)` looks like a file containing cmd's output

Process substitution gives you a file-like reference to the output. Different from command substitution which gives you the OUTPUT string. Both useful, different tools.

Common mistakes

  • Unquoted command substitution — word splits + globs the output. Quote with "$(...)" unless you want splitting.
  • Backticks in production scripts — hard to read, hard to nest. Use $(...).
  • *for f in $(ls .txt) — fragile. Use globs: for f in *.txt.
  • Forgetting trailing newlines are stripped — they always are. If you need them, store length first.
  • Capturing huge outputs — bash holds the whole thing in memory. For multi-GB outputs, pipe to a file or process line-by-line with while read.

Discussion

Ask a question, share an insight, or help someone who’s stuck.

Sign in to post a comment or reply.

Loading…