在“进程组”中启动多个终端窗口,以便在任何一个进程终止时可以杀死剩余的进程

在“进程组”中启动多个终端窗口,以便在任何一个进程终止时可以杀死剩余的进程

我有一个启动 3 个终端模拟器窗口的脚本:

#!/bin/sh

terminator --role='terminator-left' 2>/dev/null &
sleep 0.1
terminator --role='terminator-center' 2>/dev/null &
sleep 0.1
terminator --role='terminator-right' 2>/dev/null &

(我使用 --role 选项,以便我可以自动将窗口放置在不同的显示器上,在我的 openbox 配置文件中定义)

我在不同的虚拟桌面上多次启动此脚本。

如何在某种“进程组”中启动这 3 个终端,以便在其中任何一个进程终止时可以杀死所有剩余进程?

重要提示:我不想终止可能在脚本的不同实例中启动的所有终端(我不能只使用 pkill 或 pgrep 来终止与模式匹配的任何进程)

换句话说,假设我在虚拟桌面 1 和虚拟桌面 2 上启动脚本。

我现在在 2 个不同的虚拟桌面上有 3 个终端窗口。

当我退出虚拟桌面 1 上的一个终端时,我希望剩余 2 个窗口自动终止,但不会影响在虚拟桌面 2 上运行的其他 3 个窗口的独立实例。

我所描述的可能吗?

我怎样才能做到这一点?

答案1

免责声明:
我没有机会在 openbox 下测试我的建议。我仅在 kde-plasma 下验证了它,
我的解决方案依赖于waitshell 内部命令如 POSIX 程序员手册中所述。使用 -n 选项,某些特定的实现可以使脚本更简单。


#!/bin/sh

ownkill()
{
        trap - CHLD
        pkill --signal 15 -P $$ 
}

terminator --role='terminator-left' 2>/dev/null &
sleep 0.1
terminator --role='terminator-center' 2>/dev/null &
sleep 0.1
terminator --role='terminator-right' 2>/dev/null &
trap ownkill CHLD
wait

该脚本基于以下想法:

  1. 启动 3终结者根据需要进行处理,
  2. 等待它们完成,
  3. 以终止其余进程的方式处理终止上述进程之一的 SIGCHLD 信号。

注1(重置处理程序): 正如 aviro 在注释中正确观察到的那样,信号处理程序必须将 SIGCHLD 的处理重置为默认值,因为杀死剩余进程将触发其他(最多 2 个)SIGCHLD。
如果不这样做,将迫使 shell 处于某种递归情况(处理程序必须处理它自己生成的信号),根据 shell 实现或多或少地进行优雅的管理。 (从无害的警告到某些超出最大递归深度或同等。错误。)

注 2(保持 SIGCHLD 默认处理直到等待):正如 Martin Vegter 在评论中正确观察到的那样,该trap ownkill CHLD指令应紧接在等待指令之前放置。
将其作为脚本的第一条指令(正如我在前面的版本中荒谬地建议的那样)将在第一个sleep进程终止后立即触发专用处理程序。

答案2

使用 bash,您可以简单地收集所有进程 id,并在最后等待,以便当任何进程退出(bash 的wait -n)时,所有 id 都会被一些合适的信号杀死:

#!/bin/bash
terminator --role='terminator-left' 2>/dev/null &
pid1=$!
sleep 0.1
terminator --role='terminator-center' 2>/dev/null &
pid2=$!
sleep 0.1
terminator --role='terminator-right' 2>/dev/null &
pid3=$!
wait -n
kill -hup $pid1 $pid2 $pid3 2>/dev/null

由于其中一个 pid 已经失效,我们将killstderr 重定向到 null 以避免出现错误消息。

答案3

您可能愿意使用 systemd 瞬态单元,因为它的启动、控制和停止都很简单。假设您的 shell 脚本是,在其末尾./myscript添加一个wait -n命令,以便它不会立即完成,那么您可以使用以下命令启动它:

systemd-run --user --unit=mytest1 ./myscript

此命令立即返回消息Running as unit: mytest1.service,您可以使用常用命令查看创建的瞬态单元中正在运行哪些进程:

systemctl status --user mytest1

您可以使用一个命令停止所有进程:

systemctl stop --user mytest1

要多次运行同一脚本,只需确保 --unit=每次使用不同的名称即可。如果它们都以 开头test,那么您可以检查它们全部,或者通过将它们引用为 来停止它们test*

man systemd 运行了解可能有用的各种选项,例如 --setenv=--working-directory=--collect--wait。请注意,您还可以在命令末尾将参数传递给脚本systemd-run

相关内容