Does bash support forking similar to C's fork()?

81,386

Solution 1

Yes. Forking is spelled &:

echo child & echo parent

What may be confusing you is that $$ is not the PID of the shell process, it's the PID of the original shell process. The point of making it this way is that $$ is a unique identifier for a particular instance of the shell script: it doesn't change during the script's execution, and it's different from $$ in any other concurrently running script. One way to get the shell process's actual PID is sh -c 'echo $PPID'.

The control flow in the shell isn't the same as C. If in C you'd write

first(); fork(); second(); third();

then a shell equivalent is

after_fork () { second; third; }
first; after_fork & after_fork

The simple shell form first; child & parent corresponds to the usual C idiom

first(); if (fork()) parent(); else child();

& and $$ exist and behave this way in every Bourne-style shell and in (t)csh. $PPID didn't exist in the orignal Bourne shell but is in POSIX (so it's in ash, bash, ksh, zsh, …).

Solution 2

Yes, it's called subshells. Shell code inside parenthesis is run as a subshell (fork). However the first shell normally waits for the child to complete. You can make it asynchronous using the & terminator. See it in in action with something like this:

#!/bin/bash

(sleep 2; echo "subsh 1")&
echo "topsh"

$ bash subsh.sh

Solution 3

There's no native bash (or, to my knowledge, any other typical *nix shell) way of doing this. There's a lot of ways to spawn forked processes that do something else asynchronously, but I don't think there's anything that follows the exact semantics of the fork() system call.

The typical approach would be to have your top-level script spawn off helpers that do just the work you want split out. If you do $0 $@ & or whatever, you'll start at the beginning again and need to figure that out somehow.

I'm actually starting to think of several clever ways in which one might do just that....

But, before my brain gets too carried away with that, I think a pretty good rule is: if you're trying to write something in shell and it's getting full of clever tricks and you're wishing for more language features, time to switch to a real programming language.

Share:
81,386

Related videos on Youtube

Cory Klein
Author by

Cory Klein

Updated on September 17, 2022

Comments

  • Cory Klein
    Cory Klein over 1 year

    I have a script that I would like to fork at one point so two copies of the same script are running.

    For example, I would like the following bash script to exist:

    echo $$
    do_fork()
    echo $$
    

    If this bash script truly existed, the expected output would be:

    <ProcessA PID>
    <ProcessB PID>
    <ProcessA PID>
    

    or

    <ProcessA PID>
    <ProcessA PID>
    <ProcessB PID>
    

    Is there something that I can put in place of "do_fork()" to get this kind of output, or to cause the bash script to do a C-like fork?

  • mattdm
    mattdm about 13 years
    But that's basically "fork + exec", not just fork.
  • Gilles 'SO- stop being evil'
    Gilles 'SO- stop being evil' about 13 years
    @mattdm: Uh? & is fork, there's no exec involved. Fork+exec is when you launch an external command.
  • Gilles 'SO- stop being evil'
    Gilles 'SO- stop being evil' about 13 years
    @mattdm: Ah, I think I see what Cory is getting at. There's no exec, but the two languages do have different control flow.
  • mattdm
    mattdm about 13 years
    @Gilles: The bash control operator & starts a subshell in which the given command is executed. Fork + exec. You can't just put & with no preceding command to execute.
  • Gilles 'SO- stop being evil'
    Gilles 'SO- stop being evil' about 13 years
    @mattdm: Yes, the control flow is different, but exec doesn't come into play. See it with truss/trace/dtrace/strace if you don't believe me!
  • mattdm
    mattdm about 13 years
    @Gilles — that's an implementation detail because echo is a builtin. Try it with /bin/echo and of course there is an exec. But I don't mean literally; I mean conceptually. You can't fork and continue from there without giving some command — it just doesn't work like that.
  • mattdm
    mattdm about 13 years
    (I think after your edit we're basically saying the same thing, although from a different direction.)
  • Gilles 'SO- stop being evil'
    Gilles 'SO- stop being evil' about 13 years
    @mattdm: Of course invoking an external command uses an exec — but that has nothing to with the use of &. I don't understand your statement that “You can't fork and continue from there without giving some command”: similarly, you can't echo and continue from there without giving some command, the process keeps running until it calls execve(2) or _exit(2), but what does this have to do with the topic at hand?
  • mattdm
    mattdm about 13 years
    Well, if "Forking is spelled &" were a true statement, you could put & on a line by itself. But you can't. It doesn't mean "fork here". It means "execute the preceding command in the background in a subshell".
  • Gilles 'SO- stop being evil'
    Gilles 'SO- stop being evil' about 13 years
    The parentheses create a subshell, but that's fork+wait. & is fork alone. If you want to execute more than one pipeline in the child process, it's enough to use braces (which perform grouping without creating a child process): { sleep 2; echo child; } &
  • Mike S
    Mike S about 8 years
    Although your point is well taken- bash's syntax is arguably difficult and it's missing the more elegant data structures and features of Perl or Python, bash is as real as it gets- in its domain. It is a domain-specific language, and I would argue even better (more succinct, simpler) than eg Python in many instances. Can you administer a roomful of systems and not know Python? Yes. Can you do that and not know a lick of bash [programming]? I wouldn't want to try. After 30 years of shell programming I tell you it's as real as it gets. And yes, I speak Python. But don't confuse the youngsters.
  • Mike S
    Mike S about 8 years
    That said, technically I like this answer more. To say that "&" is the answer to the user's question, "fork at one point so two copies of the same script are running" is to my mind confusing. What "&" does, according to the bash manual, is "...executes the [given] command in the background in a subshell." It must terminate a command, and it does involve a fork (technically, in Linux, a clone() ), but at that point two copies of the same script are not running.
  • Gilles 'SO- stop being evil'
    Gilles 'SO- stop being evil' over 6 years
    @TheodoreMurdock Indeed, I wrote that in the wrong order by mistake. Fixed, thanks.