Shell by Example: Command Substitution POSIX

Command substitution lets you capture the output of a command and use it as a value. This is one of the most powerful features in shell scripting.

The modern syntax uses $(command). Remember to quote the command to avoid word splitting and globbing.

Edit
#!/bin/sh
current_user="$(whoami)"
echo "Current user: $current_user"
Output:
Current user: root

You can also use backticks, but $() is preferred because it’s easier to nest and read.

Edit
#!/bin/sh
current_user=`whoami`
echo "Current user: $current_user"
Output:
Current user: root

Command substitution can be used directly in strings by using the $(command) syntax.

You can also use the backticks syntax, but $() is preferred as nesting does not require escaping.

Edit
#!/bin/sh
echo "Current user: $(whoami)"
Output:
Current user: root

Command substitution can be nested with $() syntax.

This is useful when you need to capture the output of a command and use it as a value in another command.

Remember to quote the nested commands to avoid word splitting and globbing.

Edit
#!/bin/sh
echo "Shell: $(basename "$(which sh)")"
echo "Shell directory: $(dirname "$(which sh)")"
echo "Parent directory: $(dirname "$(dirname "$(which sh)")")"
Output:
Shell: sh
Shell directory: /bin
Parent directory: /

Command substitution can be also be used in conditional checks. This should be used sparingly as it can make the code harder to read when nested deeply or if the command is complex.

For more complex conditional checks, it’s better to capture the output of the command in a variable and use that in the conditional check.

Edit
#!/bin/sh
if [ "$(uname -s)" = "Linux" ]; then
    echo "Running on Linux"
elif [ "$(uname -s)" = "Darwin" ]; then
    echo "Running on macOS"
else
    echo "Running on unknown kernel"
fi
Output:
Running on Linux

Command substitution strips trailing newlines from the output. This is usually what you want.

To preserve trailing newlines, append a character and then remove it with parameter expansion.

Edit
#!/bin/sh
output="$(printf "Hello\n")"
echo "Output: '$output'"

output="$(printf "Hello\n"; printf x)"
output="${output%x}"
echo "Preserved: '$output'"
Output:
Output: 'Hello'
Preserved: 'Hello
'

Store command exit in a variable too if needed.

Edit
#!/bin/sh
result="$(ls /nonexistent 2>&1)"
exit_code="$?"
echo "Result: $result"
echo "Exit code was: $exit_code"
Output:
Result: ls: cannot access '/nonexistent': No such file or directory
Exit code was: 2

You can use command substitution in arithmetic if the output is a number.

Edit
#!/bin/sh
touch file1.txt file2.txt file3.txt
file_count="$(find . -maxdepth 1 -type f -print | wc -l)"
echo "File count: $file_count"
echo "Double the files: $((file_count * 2))"
Output:
File count: 3
Double the files: 6

« Quoting | If Statements »