83

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.

leeand00
  • 4,615

3 Answers3

119

$(…) 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 09 '18 at 19:29
  • 5
    @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 'SO- stop being evil' May 09 '18 at 19:31
  • 1
    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 09 '18 at 19:43
  • 1
    @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
  • @Gilles, is x=$(command) (which has a subshell in the rhs by definition, as you say) the only way to store the output of command into a variable? Is "command substitution without a subshell" something? – Enlico Oct 10 '19 at 18:27
  • 1
    @EnricoMariaDeAngelis It's not the only way, but it's the most natural way. Another way is command | { read line; … } (depending on the shell, line may or may not still available after the pipeline). All the ways involve a subshell because the command that produces output has to run independently of the shell that reads the input. If the command is purely internal to the shell (only shell constructs and builtins, no external commands), the shell might not create a subprocess, but that's just an optimization, it still creates a subshell. – Gilles 'SO- stop being evil' Oct 10 '19 at 18:48
  • So, long story short, all ways to accomplish command substitution imply subshell creation, right? – Enlico Oct 10 '19 at 20:19
24

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

  • 10
  • 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. – dr_ May 09 '18 at 06:55
  • 7
    @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 'SO- stop being evil' May 09 '18 at 08:17
  • Yep, on CentOS v7.4.1708 (fairly recent) bash is v4.2.46. – dr_ May 09 '18 at 12:42
9

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.

Iskustvo
  • 957