Difference of using () and $() to execute a series of commands

Okay, let's break this down. A subshell executes its contents in a chain (i.e., it groups them). This actually makes intuitive sense as a subshell is created simply by surrounding the chain of commands with (). But, aside from the contents of the subshell being grouped together in execution, you can still use a subshell as if it were a single command. That is, a subshell still has an stdin, stdout and stderr so you can pipe things to and from a subshell.

On the other hand, command substitution is not the same thing as simply chaining commands together. Rather, command substitution is meant to act a bit like a variable access but with a function call. Variables, unlike commands, do not have the standard file descriptors so you cannot pipe anything to or from a variable (generally speaking), and the same is true of command substitutions.

To try to make this more clear, what follows are a set of maybe-unclear (but accurate) examples and a set of, what I think may be, more easily-understood examples.

Let's say the date -u command gives the following:

Thu Jul  2 13:42:27 UTC 2015

But, we want to manipulate the output of this command. So, let's pipe it into something like sed:

user@host~> date -u | sed -e 's/ /    /g'
Thu    Jul        2    13:42:27    UTC    2015

Wow, that was fun! The following is completely equivalent to above (barring some environment differences that you can read about in the man pages about your shell):

user@host~> (date -u) | sed -e 's/ /    /g'
Thu    Jul        2    13:42:27    UTC    2015

That should be no surprise since all we did was group date -u. However, if we do the following, we are going to get something that may seem a bit odd at first:

user@host~> $(date -u) | sed -e 's/ /    /g'
command not found: Thu

This is because $(date -u) is equivalent to typing out exactly what date -u outputs. So the above is equivalent to the following:

user@host~> Thu Jul  2 13:42:27 UTC 2015 | sed -e 's/ /    /g'

Which will, of course, error out because Thu is not a command (at least not one I know of); and it certainly doesn't pipe anything to stdout (so sed will never get any input).

But, since we know that command substitutions act like variables, we can easily fix this problem because we know how to pipe the value of a variable into another command:

user@host~> echo $(date -u) | sed -e 's/ /    /g'
Thu    Jul        2    13:42:27    UTC    2015

But, as with any variable in bash, you should probably quote command substitutions with "".

Now, for the perhaps-simpler example; consider the following:

user@host~> pwd
/home/hypothetical
user@host~> echo pwd
pwd
user@host~> echo "$(pwd)"
/home/hypothetical
user@host~> echo "$HOME"
/home/hypothetical
user@host~> echo (pwd)
error: your shell will tell you something weird that roughly means “Whoa! you tried to have me echo something that isn't text!”
user@host~> (pwd)
/home/hypothetical

I am not sure how to describe it any simpler than that. The command substitution works just like a variable access where the subshell still operates like a command.


Subshell

(command) will execute command in a subshell. This is useful, if you have more than one command.

  • (ls) | wc will pipe the output of ls to wc, obviously you can write ls | wc.
  • (ls ; date) | wc will pipe the output of both ls and date to wc. Using ls ; date | wc will result in only date being piped to wc.

Substitution

$(command) will execute command and replace by the output. E.g.

echo $(date)

will replace $(date) by Thu Jul 2 15:20:43 CEST 2015, which results in

echo Thu Jul  2 15:20:43 CEST 2015

Putting both together

You can combine the two.

file=/hello/word
( printf "%s has %d bytes\n" "${file}" $(wc -c < "$file") ; date ) | netcat ...

Here, you can use "$file" or "${file}".

Do not forget to quote filename, in fantasy world filename are plain file, whereas in real world filename usually contains new line, space, comma, tabulation and parenthesis.