bash 是否支持类似于 C 的 fork() 的分叉?

Cor*_*ein 30 shell fork

我有一个脚本,我想在某一时刻分叉,因此同一脚本的两个副本正在运行。

例如,我希望存在以下 bash 脚本:

echo $$
do_fork()
echo $$
Run Code Online (Sandbox Code Playgroud)

如果此 bash 脚本确实存在,则预期输出将是:

<ProcessA PID>
<ProcessB PID>
<ProcessA PID>
Run Code Online (Sandbox Code Playgroud)

或者

<ProcessA PID>
<ProcessA PID>
<ProcessB PID>
Run Code Online (Sandbox Code Playgroud)

有什么我可以代替“do_fork()”来获得这种输出的东西,或者让 bash 脚本做一个类似 C 的 fork?

Gil*_*il' 27

是的。分叉拼写为&

echo child & echo parent
Run Code Online (Sandbox Code Playgroud)

可能让您感到困惑的是,这$$不是 shell 进程的 PID,而是原始shell 进程的 PID 。这样做的意义在于,它是$$特定 shell 脚本实例的唯一标识符:它在脚本执行期间不会更改,并且与$$任何其他并发运行的脚本不同。获取 shell 进程的实际 PID 的一种方法是sh -c 'echo $PPID'.

shell 中的控制流与 C 不同。如果在 C 中你会写

first(); fork(); second(); third();
Run Code Online (Sandbox Code Playgroud)

那么一个shell等价物是

after_fork () { second; third; }
first; after_fork & after_fork
Run Code Online (Sandbox Code Playgroud)

简单的 shell 形式first; child & parent对应于通常的 C 习语

first(); if (fork()) parent(); else child();
Run Code Online (Sandbox Code Playgroud)

&并且$$在每个 Bourne 风格的 shell 和 (t)csh 中都以这种方式存在和表现。$PPID在原始 Bourne shell 中不存在,但在 POSIX 中(所以它在 ash、bash、ksh、zsh 中,...)。

  • 好吧,如果“分叉拼写为 &amp;”是一个真实的陈述,那么您可以将 `&amp;` 单独放在一行中。但你不能。这并不意味着“在这里分叉”。它的意思是“在子shell的后台执行前面的命令”。 (6认同)
  • 但这基本上是“fork + exec”,而不仅仅是fork。 (3认同)
  • @Gilles:bash 控制运算符 `&amp;` 启动一个子 shell,在其中执行给定的命令。分叉+执行。您不能只放置“&amp;”而不执行前面的命令。 (2认同)
  • @Gilles - 这是一个实现细节,因为“echo”是内置的。用“/bin/echo”试试,当然有一个执行程序。但我并不是字面意义上的;而是。我的意思是概念上的。如果不给出一些命令,你就不能从那里分叉并继续——它只是不能像那样工作。 (2认同)

Kei*_*ith 11

是的,它被称为subshel​​ls。括号内的外壳代码作为子外壳(fork)运行。然而,第一个 shell 通常会等待子进程完成。您可以使用&终止符使其异步。看看它的实际效果,如下所示:

#!/bin/bash

(sleep 2; echo "subsh 1")&
echo "topsh"
Run Code Online (Sandbox Code Playgroud)

$ bash subsh.sh

  • 括号创建一个子shell,但那是fork+wait。`&amp;` 是单独的 fork。如果你想在子进程中执行多个管道,使用大括号就足够了(它在不创建子进程的情况下执行分组):`{ sleep 2; 回声孩子; } &amp;` (5认同)

mat*_*tdm 8

没有本地 bash(或者,据我所知,任何其他典型的 *nix shell)方法来执行此操作。有很多方法可以产生异步执行其他操作的分叉进程,但我认为没有任何遵循 fork() 系统调用的确切语义的方法。

典型的方法是让您的顶级脚本产生只完成您想要拆分的工作的助手。如果你这样做$0 $@ &或其他什么,你会重新开始,需要以某种方式解决这个问题。

我实际上开始想到几种聪明的方法可以做到这一点......

但是,在我的大脑对此过于痴迷之前,我认为一个很好的规则是:如果您尝试在 shell 中编写一些东西并且它充满了聪明的技巧并且您希望获得更多语言功能,那么是时候切换了到真正的编程语言

  • 尽管您的观点很好——bash 的语法可以说是困难的,并且它缺少 Perl 或 Python 更优雅的数据结构和功能,但 bash 是真实的——*在它的领域*。它是一种特定领域的语言,在许多情况下,我认为比 Python 更好(更简洁、更简单)。你能管理一屋子的系统而不知道 Python 吗?是的。你能做到这一点而不知道一点 bash [编程] 吗?我不想尝试。经过 30 年的 shell 编程,我告诉你它是真实的。是的,我会说 Python。但不要迷惑年轻人。 (2认同)
  • 也就是说,从技术上讲,我更喜欢这个答案。说“&amp;”是用户问题的答案,“一次分叉,所以同一脚本的两个副本正在运行”在我看来是令人困惑的。根据 bash 手册,“&amp;”的作用是“......在子shell的后台执行[给定]命令。” 它必须终止一个命令,它确实涉及一个 fork(从技术上讲,在 Linux 中,是一个 clone() ),但此时同一脚本的两个副本没有运行。 (2认同)