Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why does shell command “{ command1; command2: } &" open a subshell?

As we all know, placing a list of commands between curly braces causes the list to be executed in the current shell context. No subshell is created. But when using "&" after "{}", why two subshells are created? pid 1002 and 1003.

{
    ./a.out
} &

sleep 19

enter image description here

when using "./a.out &", only a subshell is created. pid 17358.

./a.out &
sleep 19

enter image description here

Why ?

like image 272
Pengcheng Avatar asked Feb 07 '23 00:02

Pengcheng


2 Answers

Background execution of a list uses a subshell because something needs to wait for each member of that list and run the next one. After a list is backgrounded, the parent shell needs to be available for new commands; it can't manage the backgrounded list too. bash can't do more than one thing at a time. So, to make the backgrounded list work, it runs a subshell.

Note that you can disown a backgrounded list and it will keep running, showing that the subshell is doing its work:

$ {
> sleep 1; sleep 2; sleep 3; sleep 4; sleep 5
> } &
$ disown
$ ps -f | grep sleep
dave     31845 31842  0 03:50 pts/1    00:00:00 sleep 3 
dave     31849 31771  0 03:50 pts/1    00:00:00 grep sleep

You could even log out and the subshell would continue running processes in the list.

When you background a single command, there is no need for a subshell because there is no more work for the shell to do after it has run the command.

In your example, the second additional bash subprocess, PID 1002, appears to be a script which you're executing. That's unrelated (conceptually, at least) to the list-backgrounding mechanism; any script in a separate file has its own bash process.

like image 129
Dave Schweisguth Avatar answered Feb 09 '23 23:02

Dave Schweisguth


If a command is terminated by the control operator &, the shell executes the command (or list of commands that are enclosed in {...}) in the background (or asynchronously) in a subshell.

The shell does not wait for the command to finish, and the return status is 0.

In C program it is done by doing a fork() followed by execvp() system calls.


Update: Based on comments below and updated question. Here is what is happening.

When you run:

./a.out &

BASH directly just runs a.out in background as running binary a.out doesn't need a separate shell process.

When you run:

{ ./a.out; } &

BASH must first fork and create a subshell as you can have series of commands inside {...} and then the newly forked subshell runs a.out in a separate process. So it is not that BASH is creating 2 subshells for this. Only one subshell gets created and 2nd pid you're seeing is for a.out.

like image 36
anubhava Avatar answered Feb 09 '23 22:02

anubhava