When the shell encounters text enclosed in
- Takes it to be a command and (as bodhi.zazen says) runs the command in a subshell.
- Substitutes the output of the command, in place of the entire expression (including the opening
$( and closing
As heartsmagic's answer explains, this is one of two available syntaxes for command substitution.
"What ... causes the output of these two commands to be the same?"
It's actually somewhat uncommon for the output of
ls to be exactly the same as the output of
ek@Io:~/tmp$ echo $(ls)
ls typically separates filenames by two or more spaces, or a newline. This helps us tell them apart more easily, especially since spaces in filenames are somewhat common (but multiple consecutive spaces, less common).
When I ran
echo $(ls), the following happened.
Command substitution replaced
You might be surprised to hear that, since that's probably not what you see when you run
ls by itself! This disparity in what
ls outputs is explained below, but is actually not the reason you end up with a single space between those two words. The same thing would happen if
$(ls) were substituted with
bar foo (with two spaces).
Afterwards, the shell performed word splitting, treating
foo as separate arguments to the
echo command instead of as a single argument containing spaces.
echo receives multiple arguments (except for options like
-n, which are treated specially and not printed at all), it prints them all out, with a single space between subsequent arguments.
echo then prints a newline (unless the
-n option was passed).
echo $(ls) shows output like
bar foo instead of
If you run
ls and it lists no files, or just one file, the output of
ls will often be the same as the output of
echo $(ls). Even then, it will not always be the same, such as when a filename contains whitespace other than isolated (single) spaces:
ek@Io:~/tmp2$ touch 'my file'
ek@Io:~/tmp2$ echo $(ls)
ls prints multiple files, its output is not likely to be exactly that same as the output of
echo a b and
echo $(echo a b) produce the same output, but
echo 'a b' and
echo $(echo 'a b') do not.
This is an important thing to know about command substitution--unquoted command substitutions are subject to word splitting.
If you want to prevent word splitting, you can enclose the your expression for command substitution in double quotes (
"). This is usually sufficient, and in particular will work with the
echo-based examples above as well as the example of
ls on a directory with a single entry containing spaces:
ek@Io:~/tmp2$ echo "$(ls)"
However, as noted above, you might be surprised to find that what you see when you run
ls is often not what gets passed to
echo in place of
ek@Io:~/tmp$ echo "$(ls)"
This is because
ls checks if standard output is a terminal to decide how to format its output, when output formatting is not explicitly specified.
- When stdout is a terminal,
ls outputs in vertically sorted columns, like
- When stdout is not a terminal,
ls lists each entry on its own line, like
In command substitution, standard output isn't a terminal because the command's output is not being sent directly to a terminal for you to see--instead, it is being captured by the shell and used as part of another command.
To get multi-column formatting when running
ls via command substitution, pass it the
ek@Io:~/tmp$ echo "$(ls -C)"
dir is sometimes suggested as an alternative to
ls -C and will also work for this, though
dir behaves like
ls -C -b rather than merely
ls may sometimes behave differently from
echo "$(ls)" is that
ls may be a shell alias. Run
alias ls to check; on Ubuntu you usually get
alias ls='ls --color=auto', which means that when
ls appears as the first word of a command you run interactively (and in some, but not all, other circumstances), it is replaced with
For example, when I run
ls, it lists directories colored blue and executables colored green (and implements many other coloring rules, too).
ls to print colored output when standard output is a terminal, and not otherwise.
To get colored output when running
ls via command substitution, pass it the
--color=always option. If you like, you can combine this with
echo $(ls -C --color)
Note that while you can make
ls an alias to
ls --color or
ls --color=always instead of
ls --color=auto, and
ls --color=always doesn't care if standard output is a terminal... that alias will still not cause
ls to produce colored output when invoked by command substitution. This is because shell aliases (in Bourne-style shells like bash) are only expanded when the shell sees them as the first word of a command (and they are not inherited by subshells).