The output of
# echo $(ls)
are the same. What exactly does the $ sign and the parenthesis mean?
Also, what is going on a technical level that causes the output of these two commands to be the same?
This is command substitution for bash.
Command substitution allows the output of a command to replace the command itself. Command substitution occurs when a command is enclosed as follows:
$(command) or `command`
And a good example similar to your question:
When the shell encounters text enclosed in
$(...)expression. Any trailing newlines are removed from this output.
$(...)was not quoted (see below).
It's actually somewhat uncommon for the output of
ls to be exactly the same as the output of
ek@Io:~/tmp$ ls bar foo ek@Io:~/tmp$ echo $(ls) bar foo
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).
Globbing (a.k.a. filename/pathname expansion) would also be performed, if the command's output had contained
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$ ls my file ek@Io:~/tmp2$ echo $(ls) my file
ls prints multiple files, its output is not likely to be exactly that same as the output of
echo a band
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 (and globbing).
If you want to prevent word splitting and globbing--and you usually will want to prevent them--you can enclose your expression for command substitution in double quotes (
"). The reason to use double quotes rather than single quotes is that single quotes are even stronger; they would suppress command substitution.
This also applies to parameter expansion, which is a more commonly used shell expansion than command substitution, and to arithmetic expansion. The reasons it is important to quote command substitutions (except in the fairly uncommon) case that you know you want further expansions to occur) are the same as the reasons to quote parameter expansion.
Double-quoting often sufficient to avoid unexpected results from command substitution. In particular, it 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)" my file
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$ ls bar foo ek@Io:~/tmp$ echo "$(ls)" bar foo
lsoutputs in vertically sorted columns, like
lslists 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)" bar foo
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'. This makes it so that, when
ls appears as the first word of a command you run interactively (and also in the far less common situation that alias expansion has been enabled in a non-interactive shell), it is replaced with
For example, when I run
ls, it lists directories colored blue and executables colored green (and observes 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.
$() are used to run commands in a subshell.
See http://tldp.org/LDP/abs/html/subshells.html for details.
Using $ in front of a variable name invokes the value assigned to the variable. Echo without the $ will just print the name of the variable to the screen (or standard out).