Running the current shell under strace(1)
and then executing e.g. <(command)
gives:
clone(child_stack=0, flags=CLONE_CHILD_CLEARTID|CLONE_CHILD_SETTID|SIGCHLD, child_tidptr=0x7fa6713d59d0) = 13305
From a purely definitional standpoint, since clone(2) is defined as
create a child process
and a subshell as
Running a shell script launches a new process, a subshell.
one could say that yes - running process substitution is invoked as a subshell.
The first part of this answer is that jobs
and dirs
are built-ins, so typically bash will just run those directly instead of invoking a subshell.
That's true for the cases where there are pipes or command substitution ($(jobs)
) or process substitution (<(jobs)
).
That's not really the case for running the command in background (jobs &
) or requesting a subshell explicitly (with ( jobs )
.)
So that explains the last part of your question. When you run jobs
in a subshell, it's indeed showing the jobs for the subshell itself.
Here's a good demonstration of this concept:
$ sleep 1001 &
[1] 15927
$ sleep 1002 &
[2] 15940
$ jobs
[1]- Running sleep 1001 &
[2]+ Running sleep 1002 &
$ ( sleep 1003 & jobs )
[1]+ Running sleep 1003 &
$ jobs
[1]- Running sleep 1001 &
[2]+ Running sleep 1002 &
You'll see that the case where jobs
is running in a subshell, only the jobs of that subshell (in this case sleep 1003
) will be displayed.
Now, to wrap up, we need to address the middle part of the question, which is why dirs &
(which does indeed run in a subshell, as would ( dirs )
) will still show the directories saved in the pushd stack.
It turns out that this is because the shell exports the list of directories in the special array variable DIRSTACK
, which is then inherited by subshells. (You can read more about DIRSTACK
here.)
One way to demonstrate how this works is to use pushd
on the subshell and see how that doesn't affect the directory stack of the original shell:
$ dirs
~
$ pushd ~/tmp
~/tmp ~
$ ( dirs; pushd / >/dev/null; dirs )
~/tmp ~
/ ~/tmp ~
$ dirs
~/tmp ~
I believe that should address all the items you asked about.
UPDATE: bash has a special provision to make jobs | less
work, having the jobs
command run in the current shell (which is not the case for other built-ins.)
The code has a check that is stored in a jobs_hack
variable, which is later checked to make it run without creating a sub-shell and allow piping the output of jobs
to another command.
See here for the relevant part of the source code that implements this. (As of bash 4.4)
Best Answer
The Learning Bash Book is wrong. Subshells inherit all variables. Even
$$
(the PID of the original shell) is kept. The reason is that for a subshell, the shell just forks and doesn't execute a new shell (on the contrary, when you type./file
, a new command is executed, e.g. a new shell; in the strace output, look atexecve
and similar). So, basically, it's just a copy (with some documented differences).Note: this is not specific to bash; this is true for any shell.