如果您在 PATH
中创建一个名为 git-mydiff
的 shell 脚本,其中包含:
#!/bin/bash
exec git diff
在一个有大量改动的仓库中调用git mydiff
,当你退出pager时,会输出:
error: git-mydiff died of signal 13
但是如果直接执行path/to/git-mydiff
,退出pager时不会报错。
显然,一种解决方案是不使用 exec
,但为什么这是个问题?为什么只有通过 git
代理命令调用脚本时才会出现问题?
我正在使用:git 版本 2.5.4 (Apple Git-61)
最佳答案
您的程序(在本例中为 mydiff
)和寻呼机(less
,或您选择的任何core.pager
)已连接通过管道。操作系统在读取器必须清理一些之前可以写入管道的数据量有一些限制,并且寻呼机在暂停之前不会读取整个管道,因此在一定数量的输出时,管道已满并且您的程序在其 write
系统调用中被阻止。
如果管道的读取端消失(通过让寻呼机退出),此时会发生两件事:操作系统向您的程序传递一个 SIGPIPE
信号,并且操作系统有 write
系统调用因 EPIPE
错误而失败。通常情况下,第一个(信号)会在第二个发生之前杀死您的程序,但如果您的程序要捕获或忽略 SIGPIPE
,则第二个会发生。
这是在 SIGPIPE
的作业控制 shell 中终止进程的示例:
> cat book.pdf | : &
>
[1] Broken pipe cat book.pdf |
Done :
(顺便说一下 :
这里是内置的冒号命令,它是一个空操作;我认为它是 Mashey shell 的遗留物,它有 goto
作为一个外部程序。)将其作为常规前台进程运行,序列是无声的:
> cat book.pdf | :
>
这是因为 shell 不会提示死于 SIGPIPE
进程,因为“死于 SIGPIPE
”是很正常的。
无论出于何种原因,git
前端都对这种死于 SIGPIPE
的情况更加嘈杂。如果您不使用 exec
,则 shell 会看到 died-of-SIGPIPE
。 shell 安静地吸收并干净地退出,git 不会提示。如果您确实使用exec
,shell 将替换为您的程序,git
前端命令会看到 died-of- SIGPIPE
状态和投诉。
一个明显的治疗方法是留下外壳。另一种方法是让外壳忽略(不捕获)SIGPIPE
,然后执行exec
:
trap "" PIPE
如你noted in a comment-reply , catching SIGPIPE
不好:因为 exec
替换了地址空间的当前占用者,操作系统重置所有caught 信号到它们的默认配置(对于 SIGPIPE
来说是“die of signal”)。但是,已忽略 信号仍会被忽略。
根据您的程序,这可能同样糟糕或更糟。例如,当 cat
死于 SIGPIPE
时,shell 是静默的,但是当 cat
看到 write
失败并返回 EPIPE
它提示:
$ cat book.pdf | :
$ trap "" PIPE
$ cat book.pdf | :
cat: stdout: Broken pipe
(这是在 FreeBSD 上;不同的操作系统略有不同,这取决于它们的实用程序有多谨慎和聪明)。
关于git - 自定义 Git 命令在执行 git diff 后死于信号 13 (SIGPIPE),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/35117004/