“kill -PIPE $$”与“exit 1”之间有什么区别?

yae*_*ael 5 linux bash kill exit

在我的 Bash 脚本中,为了退出 Bash 脚本,我使用了以下语法:

kill -PIPE $$
Run Code Online (Sandbox Code Playgroud)

我使用它是因为有时在脚本的函数或内部 shell 中,它exit 1并没有真正从 Bash 脚本中退出。

所以我用kill代替exit 1.

看起来

exit 1
Run Code Online (Sandbox Code Playgroud)

kill -PIPE $$
Run Code Online (Sandbox Code Playgroud)

几乎相等。我的假设是真的吗?

其次 - about kill -PIPE $$,使用该语法是否存在风险?

Sté*_*las 10

exit 1 使用退出代码 1 退出当前的子 shell 环境,例如:

sh -c '(echo 1; exit 1; echo 2); echo 3'
Run Code Online (Sandbox Code Playgroud)

这会exit 1退出运行代码的子shell,(...)因此2不会输出,并且父shell进程恢复 echoing 3

kill -s PIPE "$$"¹ 将 SIGPIPE 信号发送到执行当前解释脚本的 shell 解释器的进程。SIGPIPE 信号默认会导致进程终止,并且该进程已被 SIGPIPE 杀死的事实反映在其退出状态中。

所以在:

sh -c '(echo 1; kill -s PIPE "$$"; echo 2); echo 3'
Run Code Online (Sandbox Code Playgroud)

SIGPIPE 信号由子shell 进程发送到其父进程(执行 的那个进程sh)。该父进程将死亡而不输出,3而子外壳进程将继续在后台运行并输出2

如果您在bashorzsh外壳程序(以及其他一些类似 Bourne 的外壳程序)中运行该命令,这些外壳程序会将$?参数(它们对最后一个命令的退出状态的内部表示)设置为类似于 141 的值,即 128 + SIGPIPE ( 13 在大多数系统上)。

现在 SIGPIPE 是系统发送给尝试写入没有读取端的管道或套接字(损坏的管道/套接字)的进程的信号,因此将其发送到这里有点误导。为管理性终止进程保留的信号是 SIGTERM,这恰好是kill默认情况下发送的信号。

现在,您可能想要使用 SIGPIPE 而不是 SIGTERM 的一个原因是,bash当他们的一项工作被信号终止时,有类似的 shell会输出一条消息,而它们通常不会为 SIGPIPE 执行此操作(因为它很常见于SIGPIPE 终止的进程,并不一定意味着存在像cmd | head -n 1) 中的问题,因此使用 SIGPIPE 是避免这些消息的一种方法。

bash-4.4$ /bin/kill 0
Terminated
bash-4.4$ /bin/kill -s PIPE 0
bash-4.4$
Run Code Online (Sandbox Code Playgroud)

即使在非交互式时:

$ bash -c 'sh -c "kill \$\$"; exit'
bash: line 1:  6665 Terminated              sh -c "kill \$\$"
$ bash -c 'sh -c "kill -s PIPE \$\$"; exit'
$
Run Code Online (Sandbox Code Playgroud)

1此处添加缺少的引号以$$确保可靠性并使用标准(POSIX 中的非可选) kill -s PIPE语法以实现可移植性(尽管kill -PIPE也应适用于大多数系统)。