自定义 Git 命令在执行 git diff 后死于信号 13 (SIGPIPE)
Custom Git command dies from signal 13 (SIGPIPE) after exec'ing git diff
如果您在 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
)通过管道连接。 OS 在 reader 必须清理一些之前可以写入管道的数据量有一些限制,并且寻呼机在暂停之前不会读取整个管道,所以在一定数量的输出,管道已满,您的程序在其 write
系统调用中被阻塞。
如果管道的读取端消失(通过让寻呼机退出),此时会发生两件事:OS 向您的程序传递一个 SIGPIPE
信号,并且 OS 的 write
系统调用失败并出现 EPIPE
错误。通常第一个——信号——会在第二个发生之前杀死你的程序,但如果你的程序要捕获或忽略 SIGPIPE
,第二个就会发生。
这是 SIGPIPE
的作业控制 shell 中的一个示例,它终止了一个进程:
> cat book.pdf | : &
>
[1] Broken pipe cat book.pdf |
Done :
(顺便说一句 :
这里是内置的冒号命令,它是一个空操作;我认为它是 Mashey shell 的遗留物 goto
作为一个外部程序。)运行 这是一个常规的前台进程,序列是无声的:
> cat book.pdf | :
>
这是因为 shell 不会抱怨死于 SIGPIPE
进程,因为 "died of SIGPIPE
" 很正常。
无论出于何种原因,git
前端对这个死于 SIGPIPE
的案例更加嘈杂。如果不用exec
,就是shell看到死的-SIGPIPE
。 shell 安静地吸收并干净地退出并且 git 不抱怨。如果你做使用exec
,shell被你的程序替换,git
前端命令看到死的-SIGPIPE
状态和抱怨。
一个明显的治疗方法是将 shell 留在附近。另一种方法是让 shell 到 ignore(不捕获)SIGPIPE
,然后执行 exec
:
trap "" PIPE
如你,catching SIGPIPE
不好:因为exec
替换了地址[=94=的当前占用者],OS 将所有 caught 信号重置为其默认配置(对于 SIGPIPE
是 "die of signal")。但是,ignored 信号仍然被忽略。
根据您的程序,这可能同样糟糕或更糟。例如,当 cat
死于 SIGPIPE
时,shell 是沉默的,但是当 cat
看到 write
因 EPIPE
而失败时,它会抱怨:
$ cat book.pdf | :
$ trap "" PIPE
$ cat book.pdf | :
cat: stdout: Broken pipe
(这是在 FreeBSD 上;不同的 OS 会略有不同,这取决于它们的实用程序有多谨慎和聪明)。
如果您在 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
)通过管道连接。 OS 在 reader 必须清理一些之前可以写入管道的数据量有一些限制,并且寻呼机在暂停之前不会读取整个管道,所以在一定数量的输出,管道已满,您的程序在其 write
系统调用中被阻塞。
如果管道的读取端消失(通过让寻呼机退出),此时会发生两件事:OS 向您的程序传递一个 SIGPIPE
信号,并且 OS 的 write
系统调用失败并出现 EPIPE
错误。通常第一个——信号——会在第二个发生之前杀死你的程序,但如果你的程序要捕获或忽略 SIGPIPE
,第二个就会发生。
这是 SIGPIPE
的作业控制 shell 中的一个示例,它终止了一个进程:
> cat book.pdf | : &
>
[1] Broken pipe cat book.pdf |
Done :
(顺便说一句 :
这里是内置的冒号命令,它是一个空操作;我认为它是 Mashey shell 的遗留物 goto
作为一个外部程序。)运行 这是一个常规的前台进程,序列是无声的:
> cat book.pdf | :
>
这是因为 shell 不会抱怨死于 SIGPIPE
进程,因为 "died of SIGPIPE
" 很正常。
无论出于何种原因,git
前端对这个死于 SIGPIPE
的案例更加嘈杂。如果不用exec
,就是shell看到死的-SIGPIPE
。 shell 安静地吸收并干净地退出并且 git 不抱怨。如果你做使用exec
,shell被你的程序替换,git
前端命令看到死的-SIGPIPE
状态和抱怨。
一个明显的治疗方法是将 shell 留在附近。另一种方法是让 shell 到 ignore(不捕获)SIGPIPE
,然后执行 exec
:
trap "" PIPE
如你SIGPIPE
不好:因为exec
替换了地址[=94=的当前占用者],OS 将所有 caught 信号重置为其默认配置(对于 SIGPIPE
是 "die of signal")。但是,ignored 信号仍然被忽略。
根据您的程序,这可能同样糟糕或更糟。例如,当 cat
死于 SIGPIPE
时,shell 是沉默的,但是当 cat
看到 write
因 EPIPE
而失败时,它会抱怨:
$ cat book.pdf | :
$ trap "" PIPE
$ cat book.pdf | :
cat: stdout: Broken pipe
(这是在 FreeBSD 上;不同的 OS 会略有不同,这取决于它们的实用程序有多谨慎和聪明)。