Command Substitution and Expansion

By the end of this lesson you'll be able to capture a command's output and drop it straight into another command, do integer math with arithmetic expansion, supply default values and measure string length with parameter expansion, feed two command outputs to a tool with process substitution, and avoid the classic bug of unquoted output splitting apart.

Learn Command Substitution and Expansion in our free Command Line course — an interactive lesson with worked examples, a practice exercise and a quick…

Part of the free Cli course at LearnCodingFast — hands-on lessons with examples you run in your browser, plus practice exercises and a quick quiz.

1. Command Substitution: $(command)

Command substitution runs a command and replaces the $(command) expression with that command's standard output (trailing newlines are stripped). The modern form is $(...) . There is also an older form that wraps the command in backtick characters , but $(...) is preferred because it reads more clearly and nests without extra escaping.

2. Storing Output in Variables and Arguments

The captured output is just text, so it works anywhere text does. Assign it with var="$(command)" to reuse the value, or drop a $(command) directly into another command's arguments. Quoting the substitution keeps multi-word output intact, which we will return to in the quoting section.

3. Nesting Substitutions

Because $(...) nests cleanly, you can place one substitution inside another and the shell evaluates the innermost one first. This is the headline advantage over the legacy backtick form, which would force you to backslash-escape the inner command.

4. Arithmetic Expansion: $((...))

Use $((...)) for integer math. It follows normal operator precedence, and inside the double parentheses you can reference variables without a dollar sign. Remember it is integer-only: $((7 / 2)) is 3 , and % gives the remainder.

5. Parameter Expansion: defaults and length

{'$ VAR:-default '} expands to default when VAR is unset or empty , without changing VAR — perfect for fallbacks. {'$ #VAR '} expands to the length (character count) of the value, which is handy for validating input.

6. Process Substitution and Quoting

Process substitution <(command) hands a tool a filename that streams a command's output — ideal for diff <(cmd1) <(cmd2) with no temp files. And always wrap command substitution as "$(command)" : unquoted output is subject to word splitting and globbing, which silently mangles anything with spaces or newlines.

Fill in the one blank marked ___ using the # 👉 hint so the word count is captured with command substitution, then run it and check the Output panel matches.

Supply a default for an unset variable, then do integer math on the result. Fill in the blank, then run it.

No blanks this time — just a brief and an outline. Combine command substitution, arithmetic expansion, and parameter expansion into a few commands that report on a fixed phrase, and check your output against the hints in the comments.

Practice quiz

What does command substitution do?

  • Captures the output of a command and substitutes it into the command line
  • Deletes a command from history
  • Renames a command
  • Replaces a command with another command on the PATH

Answer: Captures the output of a command and substitutes it into the command line. Command substitution runs a command and replaces the $(command) expression with that command's standard output.

Which is the modern, preferred syntax for command substitution?

  • <command>
  • %command%
  • $(command)
  • {command}

Answer: $(command). $(command) is the modern POSIX form. It nests cleanly and is preferred over the older backtick form.

Why is $(...) preferred over the older backtick form?

  • It works only in zsh
  • It nests cleanly and is easier to read, while the backtick form does not nest well
  • It runs faster
  • Backticks were removed from bash

Answer: It nests cleanly and is easier to read, while the backtick form does not nest well. $(...) can be nested directly and avoids the confusing backslash-escaping the backtick form needs when nested.

What is the result of the arithmetic expansion $((4 + 5 * 2))?

  • 26
  • 11
  • 18
  • 14

Answer: 14. $((...)) does integer arithmetic with normal precedence, so 5 * 2 is 10, plus 4 is 14.

What does ${name:-guest} expand to when name is unset or empty?

  • guest
  • An empty string
  • An error
  • The literal text name

Answer: guest. ${VAR:-default} uses the default value when VAR is unset or empty; otherwise it uses VAR's value.

If word=hello, what does ${#word} produce?

  • 1
  • 5
  • hello
  • h

Answer: 5. ${#VAR} expands to the length of the variable's value, and hello has 5 characters.

Why should you wrap command substitution in double quotes, as in "$(cmd)"?

  • It makes the command run faster
  • It is required for the command to run at all
  • It converts the output to uppercase
  • It prevents word splitting and globbing, preserving whitespace and newlines in the output

Answer: It prevents word splitting and globbing, preserving whitespace and newlines in the output. Quoting with "$(cmd)" stops the shell from splitting the output on whitespace and from expanding glob characters, keeping the result intact.

What does process substitution <(command) provide to the command using it?

  • Nothing; it is a comment
  • The exit status of command
  • A filename that, when read, yields the output of command
  • The process ID of command

Answer: A filename that, when read, yields the output of command. <(command) gives a filename (often /dev/fd/63) that streams the command's output, so tools expecting a file path can read it.

Which command compares the output of two commands without creating temporary files?

  • diff $(cmd1) $(cmd2)
  • diff <(cmd1) <(cmd2)
  • diff ((cmd1)) ((cmd2))
  • diff cmd1 cmd2

Answer: diff <(cmd1) <(cmd2). diff <(cmd1) <(cmd2) uses process substitution to feed each command's output to diff as a file.

What does count=$(grep -c root /etc/passwd) store in count?

  • The number printed by the grep command
  • The text grep -c root /etc/passwd
  • The exit status of grep
  • Nothing, because grep cannot be substituted

Answer: The number printed by the grep command. Command substitution in an assignment stores the command's standard output, so count receives the number grep -c printed.