执行 git diff 后,自定义 Git 命令因信号 13 (SIGPIPE) 而终止

red*_*ree 2 unix git signals process exec

如果您在PATH调用中创建一个 shell 脚本git-mydiff,其中包含:

#!/bin/bash
exec git diff
Run Code Online (Sandbox Code Playgroud)

git mydiff并在一个有大量更改的存储库中调用,当退出分页器时,它会输出:

error: git-mydiff died of signal 13
Run Code Online (Sandbox Code Playgroud)

但如果path/to/git-mydiff直接执行,退出分页器时就不会报错。

显然,一种解决方案是不使用exec,但为什么这是一个问题呢?为什么只有通过代理命令调用脚本时才会出现问题git

我正在使用:git 版本 2.5.4 (Apple Git-61)

tor*_*rek 5

在本例中,您的程序mydiff和寻呼机(less或您选择的任何内容core.pager)通过管道连接。在读取器必须清除一些数据之前,操作系统对可以写入管道的数据量有一些限制,并且分页器在暂停之前不会读取整个管道,因此在输出一定量时,管道已填满并且您的程序在其write系统调用中被阻止。

\n\n

如果管道的读取端消失(通过分页器退出),此时会发生两件事:操作系统SIGPIPE向您的程序发送信号,并且操作系统使write系统调用失败并出现EPIPE错误。通常,第一个\xe2\x80\x94信号\xe2\x80\x94会在第二个发生之前杀死你的程序,但如果你的程序要捕获或忽略SIGPIPE,第二个就会发生。

\n\n

下面是一个在作业控制 shell 中SIGPIPE终止进程的示例:

\n\n
> cat book.pdf | : &\n>\n[1]    Broken pipe                   cat book.pdf |\n       Done                          :\n
Run Code Online (Sandbox Code Playgroud)\n\n

(顺便说一句,:这里是内置的冒号命令,它是一个无操作;我认为它是作为goto外部程序的 Mashey shell 的剩余部分。)将其作为常规前台进程运行,序列是无声的:

\n\n
> cat book.pdf | :\n>\n
Run Code Online (Sandbox Code Playgroud)\n\n

这是因为 shell 不会抱怨进程死亡SIGPIPE,因为“死亡SIGPIPE”是很正常的。

\n\n

不管出于什么原因,git前端对这起死亡案件的争论更加激烈SIGPIPE。如果您不使用exec,则shell会看到 dead-of- SIGPIPE。shell 安静地吸收它并干净地退出,并且 git 不会抱怨。如果您确实使用exec,shell 会被替换为您的程序,并且git前端命令会看到死亡SIGPIPE状态并发出抱怨。

\n\n

一种明显的治疗方法是保留外壳。另一种方法是让 shell 忽略而不是捕获)SIGPIPE,然后执行以下操作exec

\n\n
trap "" PIPE\n
Run Code Online (Sandbox Code Playgroud)\n\n

正如您在 comment-reply 中指出的那样捕获 SIGPIPE并不好:因为exec替换了地址空间的当前占用者,所以操作系统将所有捕获的信号重置为其默认配置(即SIGPIPE“信号死亡”)。然而,被忽略的信号仍然被忽略。

\n\n

根据您的程序,这可能会同样糟糕或更糟。例如,当shellcat死亡时,它是沉默的,但当看到失败时,它会抱怨:SIGPIPEcatwriteEPIPE

\n\n
$ cat book.pdf | :\n$ trap "" PIPE\n$ cat book.pdf | :\ncat: stdout: Broken pipe\n
Run Code Online (Sandbox Code Playgroud)\n\n

(这是在 FreeBSD 上;不同的操作系统略有不同,具体取决于它们的实用程序的仔细和聪明程度)。

\n