运行多个命令并将它们作为一个在 bash 中杀死

use*_*909 70 command-line bash process background-process process-groups

我想在单个 shell 上运行多个命令(进程)。都有自己的连续输出,不停歇。在后台运行它们Ctrl- C。我想将它们作为单个进程(子shell,也许?)运行,以便能够用Ctrl-停止所有这些进程C

具体来说,我想使用mocha(监视模式)运行单元测试,运行服务器并运行一些文件预处理(监视模式),并在一个终端窗口中查看每个测试的输出。基本上我想避免使用一些任务运行器。

我可以通过在后台运行进程 ( &)来实现它,但是我必须将它们放到前台来阻止它们。我想要一个包装它们的过程,当我停止这个过程时,它会停止它的“孩子”。

Min*_*nix 83

要同时运行命令,您可以使用&命令分隔符。

~$ command1 & command2 & command3
Run Code Online (Sandbox Code Playgroud)

这将启动command1,然后在后台运行它。与 相同command2。然后它command3正常启动。

所有命令的输出都会乱码,但如果这对您来说不是问题,那就是解决方案。

如果您想稍后单独查看输出,您可以将每个命令的输出通过管道tee传输到 中,这样您就可以指定将输出镜像到的文件。

~$ command1 | tee 1.log & command2 | tee 2.log & command3 | tee 3.log
Run Code Online (Sandbox Code Playgroud)

输出可能会非常混乱。为了解决这个问题,您可以使用sed.

~$ echo 'Output of command 1' | sed -e 's/^/[Command1] /' 
[Command1] Output of command 1
Run Code Online (Sandbox Code Playgroud)

因此,如果我们将所有这些放在一起,我们会得到:

~$ command1 | tee 1.log | sed -e 's/^/[Command1] /' & command2 | tee 2.log | sed -e 's/^/[Command2] /' & command3 | tee 3.log | sed -e 's/^/[Command3] /'
[Command1] Starting command1
[Command2] Starting command2
[Command1] Finished
[Command3] Starting command3
Run Code Online (Sandbox Code Playgroud)

这是您可能会看到的高度理想化的版本。但它是我现在能想到的最好的。

如果你想一次停止所有这些,你可以使用trap.

~$ trap 'kill %1; kill %2' SIGINT
~$ command1 & command2 & command3
Run Code Online (Sandbox Code Playgroud)

这将执行command1,并command2在后台command3在前台,它可以让你杀了它Ctrl+ C

当你用Ctrl+杀死最后一个进程时Ckill %1; kill %2命令会被执行,因为我们将它们的执行与一个 INTerupt 信号的接收联系起来,这个信号是通过按Ctrl+发送的C

它们分别杀死第一个和第二个后台进程(您的command1command2)。使用 完成命令后,不要忘记移除陷阱trap - SIGINT

命令的完整怪物:

~$ trap 'kill %1; kill %2' SIGINT
~$ command1 | tee 1.log | sed -e 's/^/[Command1] /' & command2 | tee 2.log | sed -e 's/^/[Command2] /' & command3 | tee 3.log | sed -e 's/^/[Command3] /'
Run Code Online (Sandbox Code Playgroud)

当然,你可以看看screen。它可以让您根据需要将控制台拆分为多个单独的控制台。因此,您可以分别监视所有命令,但同时监视。

  • 期待大家的批评。:) (3认同)
  • 这是一个相当聪明的解决方案,iv 学到了一些新东西,bravo +1 (3认同)
  • 谢谢你。它完全符合我的要求(trap 命令)。尽管如此,该解决方案必须包含在脚本中以实现可重用性。 (2认同)

Gil*_*il' 30

如果您安排在同一进程组中运行它们(并且仅运行它们),您可以轻松地一次杀死一堆进程。

Linux 提供了setsid在新进程组中运行程序的实用程序(甚至在新会话中,但我们不在乎)。(这是可行的,但没有setsid. 会更复杂。)

进程组的进程组ID(PGID)是该组中原始父进程的进程ID。要杀死进程组中的所有进程,请将 PGID 的负数传递给kill系统调用或命令。即使具有此 PID 的原始进程死亡,PGID 仍然有效(尽管它可能有点混乱)。

setsid sh -c 'command1 & command2 & command3' &
pgid=$!
echo "Background tasks are running in process group $pgid, kill with kill -TERM -$pgid"
Run Code Online (Sandbox Code Playgroud)

如果您从非交互式shell在后台运行进程,那么它们都将保留在 shell 的进程组中。只有在交互式 shell 中,后台进程才能在它们自己的进程组中运行。因此,如果您从保留在前台的非交互式 shell 中分叉命令,Ctrl+C将杀死它们。使用wait内置命令让 shell 等待所有命令退出。

sh -c 'command1 & command2 & command3 & wait'
# Press Ctrl+C to kill them all
Run Code Online (Sandbox Code Playgroud)

  • 低估的答案(底部的方法)。易于理解和记忆。 (4认同)

小智 25

我很惊讶这还不在这里,但我最喜欢的方法是使用带有后台命令的子shell。用法:

(command1 & command2 & command3)

两者都可以看到输出,所有 bash 命令和便利工具仍然可用,一个就可以Ctrl-c杀死它们。我通常使用它同时启动实时调试客户端文件服务器和后端服务器,因此我可以在需要时轻松杀死它们。

其工作原理是,command1command2在一个新的子shell实例的后台运行,并command3在该实例的前景,以及子shell是什么第一次收到一个杀信号Ctrl-c按键。关于谁拥有什么进程以及后台程序何时被杀死,这非常类似于运行 bash 脚本。

  • 如果您将 `wait` 添加为最终命令(在您的示例中为 command3),您可以在用户按下 Ctrl-c 后执行 _clean up_ 代码。 (3认同)