48

I understand the subshell syntax to be (<commands...>), is $() just a subshell that you can retrieve variable values from?

Note: This applies to bash 4.4 based on different wording in their documentation.

70

$(…) is a subshell by definition: it's a copy of the shell runtime state¹, and changes to the state made in the subshell have no impact on the parent. A subshell is typically implemented by forking a new process (but some shells may optimize this in some cases).

It isn't a subshell that you can retrieve variable values from. If changes to variables had an impact on the parent, it wouldn't be a subshell. It's a subshell whose output the parent can retrieve. The subshell created by $(…) has its standard output set to a pipe, and the parent reads from that pipe and collects the output.

There are several other constructs that create a subshell. I think this is the full list for bash:

  • Subshell for grouping: ( … ) does nothing but create a subshell and wait for it to terminate). Contrast with { … } which groups commands purely for syntactic purposes and does not create a subshell.
  • Background: … & creates a subshell and does not wait for it to terminate.
  • Pipeline: … | … creates two subshells, one for the left-hand side and one for the right-hand side, and waits for both to terminate. The shell creates a pipe and connects the left-hand side's standard output to the write end of the pipe and the right-hand side's standard input to the read end. In some shells (ksh88, ksh93, zsh, bash with the lastpipe option set and effective), the right-hand side runs in the original shell, so the pipeline construct only creates one subshell.
  • Command substitution: $(…) (also spelled `…`) creates a subshell with its standard output set to a pipe, collects the output in the parent and expands to that output, minus its trailing newlines. (And the output may be further subject to splitting and globbing, but that's another story.)
  • Process substitution: <(…) creates a subshell with its standard output set to a pipe and expands to the name of the pipe. The parent (or some other process) may open the pipe to communicate with the subshell. >(…) does the same but with the pipe on standard input.
  • Coprocess: coproc … creates a subshell and does not wait for it to terminate. The subshell's standard input and output are each set to a pipe with the parent being connected to the other end of each pipe.

¹ As opposed to running a separate shell.

  • Could you also include ${...} in the answer? – user1717828 May 9 '18 at 19:29
  • 2
    @user1717828 What? Why? What does variable expansion remotely have to do with this question? I'm not going to include the whole shell manual in my answer. – Gilles May 9 '18 at 19:31
  • What does variable expansion remotely have to do with this question? I don't know, that's why I asked :-) So I'm guessing the curly brace substitution is nothing like the parenthesis brace substitution. – user1717828 May 9 '18 at 19:43
  • @user1717828: variable expansion is unrelated to subshells; it's a separate mechanism altogether, and definitely worth reading into if you're just starting out! – 0xdd May 10 '18 at 16:47
20

From the bash(1) man page in bash version 4.4, "EXPANSION" section, "Command Substitution" subsection:

Bash performs the expansion by executing command in a subshell environment [...]

  • 9
    This is also explicitly specified by POSIX. – Stephen Kitt May 9 '18 at 6:36
  • 1
    Interestingly, under CentOS 7 the bash manpage doesn't mention any subshell: Bash performs the expansion by executing command and replacing the command substitution with the standard output of the command, with any trailing newlines deleted. I wonder if this was a deliberate omission. – dr01 May 9 '18 at 6:55
  • 6
    @dr01 On the contrary, bash 4.4 changed the wording of that sentence to include the word “subshell”. It was a clarification: the manual explicitly mentioned that various other constructs were subshells, but until 4.4 it wasn't explicitly stated for command substitution. – Gilles May 9 '18 at 8:17
  • Yep, on CentOS v7.4.1708 (fairly recent) bash is v4.2.46. – dr01 May 9 '18 at 12:42
5

Yes, ( commands... ) is a bash subshell that will execute commands... in another process.

The only difference when you have $( commands... ) is that this part of code will after execution of commands... be replaced with everything that commands... wrote to stdout.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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