If I am correct, command substitution, process substitution, pipeline, and background jobs run the commands specified within themselves in a subshell not in the original shell.

But when the commands are jobs or dirs, they output exactly the same as when they are run directly in the original shell:

echo $( jobs )

cat <(jobs)

jobs | less

echo $( dirs )

cat <(dirs)

dirs | less

dirs &

Why is that? Is it because the subshells inherit the jobs and dirs stack from the original shell, or because jobs and dirs are run in the original shell instead of the subshells?


jobs &

doesn't output anything. Why is it different from the other commands? Thanks.

Related Does sourcing a script in subshells still allow the commands in the script to access the state of the original shell?

1 Answer 1


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)

  • 1
    where do you get the basis for your initial argument? in my experience bash is particularly notorious for needless childing... i would assume $! is a major factor here, and maybe a sloppy handover/lazy check where $$ remains unaltered. too lazy to check now, though.
    – mikeserv
    Jul 28, 2018 at 7:30
  • Thanks. "the shell exports the list of directories in the special array variable DIRSTACK", but env doesn't show DIRSTACK
    – Tim
    Jul 30, 2018 at 20:58
  • Do you find any general rule about which other builtin commands used in either of the five cases are run in subshells or in original shells?
    – Tim
    Jul 30, 2018 at 21:08
  • In cd .. | pwd, cd .. runs in a subshell not in the original shell. Why is it different from jobs | cat?
    – Tim
    Jul 30, 2018 at 22:59
  • Hi @Tim, I updated the answer to address that. Indeed, there's a special case for jobs, bash will recognize it and force it to run in the current shell process, exactly in order to support this use case, of piping its output to another command. I hope that helps!
    – filbranden
    Jul 30, 2018 at 23:27

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.