Take the 2-minute tour ×
Ask Ubuntu is a question and answer site for Ubuntu users and developers. It's 100% free, no registration required.

The output of

# ls

and

# 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?

share|improve this question

4 Answers 4

up vote 4 down vote accepted

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/

share|improve this answer

() and $() are used to run commands in a subshell

See http://tldp.org/LDP/abs/html/subshells.html for details

share|improve this answer

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).

share|improve this answer
    
(ls) is not a variable name. –  Eliah Kagan Oct 16 at 22:40

When the shell encounters text enclosed in $( ), it:

  1. Takes it to be a command and (as bodhi.zazen says) runs the command in a subshell.
  2. 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 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.

  1. 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 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).

  2. Afterwards, the shell performed word splitting, treating bar and foo as separate arguments to the echo command instead of as a single argument containing spaces.

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 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.

Terminal Detection

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)"
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, like ls -C.
  • When stdout is not a terminal, ls lists each entry on its own line, like ls -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', 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 ls --color=auto.

For example, when I run ls, it lists directories colored blue and executables colored green (and implements 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).

share|improve this answer

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.