Does bash support forking similar to C's fork()?
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.
Related videos on Youtube
Cory Klein
Updated on September 17, 2022Comments
-
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 about 13 yearsBut that's basically "fork + exec", not just fork.
-
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' 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 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' about 13 years@mattdm: Yes, the control flow is different, but
exec
doesn't come into play. See it withtruss
/trace
/dtrace
/strace
if you don't believe me! -
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 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' 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'techo
and continue from there without giving some command, the process keeps running until it callsexecve(2)
or_exit(2)
, but what does this have to do with the topic at hand? -
mattdm about 13 yearsWell, 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' about 13 yearsThe 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 about 8 yearsAlthough 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 about 8 yearsThat 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' over 6 years@TheodoreMurdock Indeed, I wrote that in the wrong order by mistake. Fixed, thanks.