1. Overview

When we have complex shell scripts, we often need to use the output of other commands in our logic.

In this quick tutorial, we’ll see how to do this with command substitution.

2. Syntax

Let’s first take a look at the syntax. Due to historical reasons, we can use two types of syntax:

$(command) 

`command`

The first form is recommended because it solves an inconsistent behavior. It is also much more readable.

We should keep in mind that command substitution spawns a sub-shell.

Let’s see a simple example:

$ echo "Current path is ["$(pwd)"]"
Current path is [/home/baeldung]

This also works with the old syntax:

$ echo "Current path is ["`pwd`"]"
Current path is [/home/baeldung]

In this example, the output of the pwd command replaces the actual command.

So what is the difference between the formats?

Let’s consider this example:

$ function different_syntax() {
    echo "Backticks ["`echo \\baeldung`"]"
    echo "Dollar ["$(echo \\baeldung)"]"
}

$ different_syntax
Backticks [baeldung]
Dollar [\baeldung]

The form handles the character differently. To be specific, it behaves differently when followed by $, or .

Consider that we have a variable pointing to a directory that exists:

$ function different_syntax() {
    dir="/home"
    echo "Backticks ["`ls \$dir`"]"
    echo "Dollar ["$(ls \$dir)"]"
}

$ different_syntax
Backticks [baeldung demo]
ls: cannot access '$dir': No such file or directory
Dollar []

In the first case, the does not escape the $ character and the variable is expanded to its value.

3. Quoting and Nesting

Now that we’ve seen the basic syntax, let’s see some advanced scenarios.

We can use nesting in a command substitution expression:

$ function nesting() {
    current_folders=$(ls $(pwd))
    echo "Current folders in $(pwd) ["$current_folders"]"
}

$ nesting
Current folders in /home [baeldung demo]

First, the current path replaces the pwd command. Then, we pass this to ls and substitute the whole expression with its output. Finally, we store the output in a variable.

When we use double quotes, Bash does not perform word splitting or filename expansion:

$ function quoting() {
    for element in $(ls $(pwd))
        do 
            echo "["$element"]"
        done
}

$ quoting
[baeldung]
[demo]

In this case, we iterate over all the files in the current directory.

Now let’s see what happens if we use quotes in our loop:

$ function quoting() {
    for element in "$(ls $(pwd))"
        do 
            echo "["$element"]"
        done
}

$ quoting
[baeldung demo]

Since word splitting did not occur, we iterate over a single element.

Let’s finish with a useful snippet:

$ ps -g $(ps -C firefox -o sid=)
  PID TTY          TIME CMD
 1652 ?        00:02:48 gnome-shell
 1815 ?        00:00:12 Xwayland
 2207 ?        00:00:09 ibus-daemon
 2214 ?        00:00:00 ibus-dconf
 2215 ?        00:00:02 ibus-extension-
 2217 ?        00:00:00 ibus-x11
 2232 ?        00:00:02 ibus-engine-sim
 2533 ?        00:09:36 firefox
 2670 ?        00:00:32 WebExtensions
...

Let’s see what it does. First, we search a particular process using the ps command and print its session id.

We use the = after the output specifier to suppress the headers in the nested command.

We then use this output (the session id) to print all the processes in this particular session.

4. Conclusion

In this quick tutorial, we saw how to use command substitution.

First, we talked about the two types of basic syntax and discussed the differences between them.

After that, we saw how command-substitution nesting works.

Finally, we explained how quoting affects the results of command substitution.