运行多个命令并将其作为bash中的一个杀死


51

我想在单个shell上运行多个命令(进程)。它们都有自己的连续输出并且不会停止。在后台运行它们会中断Ctrl- C。我想将它们作为单个进程(可能是subshel​​l)运行,以便能够使用Ctrl- 停止所有进程C

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

我可以通过在后台(&)中运行进程来实现这一点,但随后必须将其置于前台以停止它们。我希望有一个包装它们的过程,当我停止该过程时,它将停止其“子代”。


它们应该同时运行还是一个接一个地运行?
Minix 2015年

是的,进程应该同时运行,但是我需要查看每个进程的输出。
user1876909

Answers:


67

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

~$ command1 & command2 & command3

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

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

如果以后要单独查看输出,可以将每个命令的输出通过管道tee传输到,从而可以指定一个文件来镜像输出。

~$ command1 | tee 1.log & command2 | tee 2.log & command3 | tee 3.log

输出可能会很混乱。为了解决这个问题,您可以使用来给每个命令的输出加上前缀sed

~$ echo 'Output of command 1' | sed -e 's/^/[Command1] /' 
[Command1] Output of command 1

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

~$ 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

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

如果您想一次停止所有这些,则可以使用build in trap

~$ trap 'kill %1; kill %2' SIGINT
~$ command1 & command2 & command3

这将执行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] /'

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


3
期待所有批评。:)
Minix 2015年

2
谢谢。它完全符合我的要求(trap命令)。但是,该解决方案必须包装在脚本中以实现可重用性。
user1876909 2015年

@ user1876909这是一个骨架。具体取决于您[1]。很高兴我能帮上忙。[1] pastebin.com/jKhtwF40
Minix

3
这是一个相当聪明的解决方案,我从中学到了一些新东西,赞不绝口+1
mike-m

1
这真太了不起了。我有很多要研究的答案并从中学习。
ccnokes '17

20

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

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"

如果您从非交互式 Shell 在后台运行进程,则它们将全部保留在Shell的进程组中。仅在交互式外壳中,后台进程才能在其自己的进程组中运行。因此,如果您从非交互shell中保留命令,而该shell仍保留在前台,则Ctrl+ C将杀死它们。使用wait内置函数使外壳程序等待所有命令退出。

sh -c 'command1 & command2 & command3 & wait'
# Press Ctrl+C to kill them all

2
无延误的答案(底部的方法)。易于理解和记忆。
Nicolas Marshall

14

我很惊讶这还没到,但是我最喜欢的方法是在后台命令中使用子shell。用法:

(command1 & command2 & command3)

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

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


如果添加wait为最终命令(在示例中为command3),则可以在用户按下Ctrl-c之后执行清理代码。
dotnetCarpenter '18


-2

您可以使用pipe。这将同时在管道的两端启动命令。您也应该能够停止所有命令CTRL-C

1st command | 2nd command | 3rd command

如果要一个接一个地运行命令,可以使用@serenesat的方法。


这会将第一条命令的输出传递给第二条命令(以此类推),这在这里不合适,因为应该将第一条命令的输出终止在终端上。
吉尔斯(Gilles)'“ SO-别再邪恶了”
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.