What do the paranthesis and $ sign mean in echo $(ls)
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`
http://www.gnu.org/software/bash/manual/bashref.html#Command-Substitution
More:
http://tldp.org/LDP/abs/html/commandsub.html
http://wiki.bash-hackers.org/syntax/expansion/cmdsubst
And a good example similar to your question:
http://bashshell.net/shell-scripts/using-command-substitution-in-a-bash-shell-script/
When the shell encounters text enclosed in $(
)
, it:
- Takes it to be a command and (as Panther says) runs the command in a subshell.
- Substitutes the output of the command, in place of the entire
$(...)
expression. Any trailing newlines are removed from this output. - Performs further expansions if
$(...)
was not quoted (see below).
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 echo $(ls)
:
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).
Word Splitting
When I ran echo $(ls)
, the following happened.
Command substitution replaced
$(ls)
with:bar foo
You might be surprised to hear that, since that's probably not what you see when you run
ls
by itself! This disparity in whatls
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 withbar foo
(with two spaces).Afterwards, the shell performed word splitting, treating
bar
andfoo
as separate arguments to theecho
command instead of as a single argument containing spaces.Globbing (a.k.a. filename/pathname expansion) would also be performed, if the command's output had contained
*
,?
, or[
.As detailed below, quoting with double quotes permits command substitution but suppresses word splitting and globbing.
When 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.
Thus echo $(ls)
shows output like bar foo
instead of bar foo
.
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
When ls
prints multiple files, its output is not likely to be exactly that same as the output of $(ls)
. Similarly:
echo a b
andecho $(echo a b)
produce the same output, butecho 'a b'
andecho $(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).
Quoting
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.
Terminal Detection
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 "$(ls)"
:
ek@Io:~/tmp$ ls
bar foo
ek@Io:~/tmp$ echo "$(ls)"
bar
foo
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, likels -C
. - When stdout is not a terminal,
ls
lists each entry on its own line, likels -1
.
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 -C
flag:
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 -C
.)
Shell Aliasing
Another reason 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 ls --color=auto
.
For example, when I run ls
, it lists directories colored blue and executables colored green (and observes many other coloring rules, too). --color=auto
causes 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
or --color=always
option. If you like, you can combine this with -C
:
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.