Is $() a subshell?
$(…)
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 thelastpipe
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.
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 [...]
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
.