4

Every time we run a normal command like ps multiple times, it has different process ids (PID). I wanted to know if that means for every command we run in terminal (bash) it creates a child process of bash to execute that command.

3 Answers 3

8

Yes, pretty much every command you'd run on the command line runs in a process of its own, and those processes are children of the shell launching them.

The exception here are builtin commands of the shell. Bash implements some standard utilities itself, like printf, echo, true, false, kill and [/test, so running those doesn't involve forking a child. The same applies to things like cd, read and mapfile, though they affect the shell's internal state, so they have to be builtin.

(Also break, continue, and return, which oddly enough are builtin utilities, not shell keywords like if and while.)

There's really no way for the shell to run an external program in the same process with itself, while still being able to go back. It is however possible for the shell to replace itself with another program. E.g. if you run echo $$ to see the PID of the shell and subsequently run exec ps, you'll see ps running with the same PID. And when ps exits, that shell no longer exists. Actually, a similar thing happens every time you run a program in the normal way, just that the shell makes a copy of itself (the fork() system call) before replacing the child with the program to run (execve()). In between the shell program running in the child process takes care of setting up any redirections and such for the child.

It would be possible for a shell to implement other tools as builtins too, e.g. Busybox implements are largish set of standard utilities in the same program file. But as far as I tested, it still forks a child when running them, probably since it's an easy way to ensure the utilities don't mess with the shell's state unnecessarily.

4

Yes, for almost any command [1].

Except for the commands implemented as built-ins, the list of which you can get with help in bash [2].

Notice that bash also forks when running subshells, even if they only run builtins; e.g. in (echo a | read p) you have 3 separate processes on top of the one running the main shell.

[1] The initial idea with the very first Unix shell (the Thompson shell) was that all commands should be separate binaries and run as separate processes. However, design limitations and implementation quirks caused that to quicky fall apart, as first chdir had to be implemented as a built-in, then the variable assignments, and so on. Further shells eroded the initial idea more and more.

[2] Even when running buitins, bash tries to mimic separate processes; for instance the redirection from echo > file is handled in a totally different (and very complicated) way than the one from ls > file in order to maintain the illusion that echo runs just as a separate command, and the redirection doesn't apply to the entire shell, but only to the echo command. Bash does not always get that right; for instance, in

bash -c 'trap "echo Ctrl-C" INT; read p; echo DONE'

the INT trap will not be executed after the read command has returned, as it would happen with any other command (other shells like dash do get that right). Also, a blocking built-in echo may return with a silly "Interrupted system call" something that an external /bin/echo will never do (since echo doesn't have to and doesn't set any signal handler ;-)) -- and thousands of similar cases where the kludge shows through the seams.

NB: busybox has nothing to do with this -- busybox is an example of multi-call binary, a program which can execute different functions depending on the first command line argument; just like ex/vi, od/hexdump, etc. However, the applets from busybox (including its shells, ash and hush) are able to run some other applets as same process built-ins, if expressly compiled that way (the NOFORK/NOEXEC applets).

2
  • (echo a | read p) you have 3 separate processes on top of the one running the main shell. Why three? should be two current shell with "echo a" and child with "read p"
    – Saboteur
    Nov 5, 2021 at 15:59
  • No, they're three (four if you also count the main shell). There's one process for the subshell created by the parens ((...)), one for the left side (...|) of the pipeline, and one for the right side (|...).
    – user313992
    Nov 5, 2021 at 18:15
-2

Most normal things you run in bash do get a new PID because they are different processes (a process starts with a given PID, it runs, then it finishes, the PID is available for a new process... eventually... check zombie processes). There are some builtins that are directly run by bash without creating a new process (echo, for example).... and then there are some things that create bash subprocesses (each with their own PID, like while read, that won't allow you to set the value of a variable inside the while to use it outside because the new value dies when while is done).

3
  • 3
    There's nothing special about while, it doesn't create a subshell by itself. You're thinking of pipes a | b | c | while read ...; done -- each command in a pipeline runs in a separate subshell. See 3.2.3 Pipelines in the manual Nov 4, 2021 at 20:40
  • This does not answer whether each command that is executed is started by first starting a bash shell process to execute it, which seems to be the question. I don't know if the user in the question wonders whether fork() is called internally, which in effect creates a copy of the shell process, or whether they think bash invokes the bash utility to run each command.
    – Kusalananda
    Nov 4, 2021 at 20:47
  • @glennjackman That is great insight. Let me adjust that in my answer
    – eftshift0
    Nov 4, 2021 at 20:55

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .