在执行git diff之后,自定义Git命令从信号13(SIGPIPE)中消失

时间:2016-01-31 17:38:29

标签: git unix process signals exec

如果您在名为PATH的{​​{1}}中创建了一个包含以下内容的shell脚本:

git-mydiff

并在具有大量更改的存储库中调用#!/bin/bash exec git diff ,当您退出寻呼机时,它将输出:

git mydiff

但是,如果直接执行error: git-mydiff died of signal 13 ,退出寻呼机时将不会出现错误。

显然,一种解决方案是不使用path/to/git-mydiff,但为什么这是一个问题?为什么通过exec代理命令调用脚本只是一个问题?

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

1 个答案:

答案 0 :(得分:1)

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

如果管道的读取端消失(通过让寻呼机退出),此时会发生两件事:操作系统向您的程序发送SIGPIPE信号,操作系统具有{{1}系统调用失败,出现write错误。通常情况下,第一个信号会在第二个偶然发生之前杀死你的程序,但是如果你的程序要捕获或忽略EPIPE,那么第二个就会发生。

以下是SIGPIPE作业控制shell中杀死进程的示例:

SIGPIPE

(顺便提一下> cat book.pdf | : & > [1] Broken pipe cat book.pdf | Done : 这里是内置的冒号命令,这是一个无操作;我认为,它是一个剩余的,我认为,Mashey shell有:作为外部程序。)运行这是一个常规的前台进程,序列是沉默的:

goto

这是因为shell没有抱怨死亡的> cat book.pdf | : > 进程,因为“死于SIGPIPE”是很正常的。

无论出于何种原因,SIGPIPE前端对这个死亡的git案件更为吵闹。如果您不使用SIGPIPE,则 shell 会看到死亡的exec。外壳悄悄地吸收并且干净地离开,并且git不会抱怨。如果您执行使用SIGPIPE,则shell将替换为您的程序,而exec前端命令会看到死亡的git状态和抱怨。

一个明显的治疗方法是将壳留在身边。另一种方法是让shell 忽略(不捕获)SIGPIPE,然后执行SIGPIPE

exec

当你noted in a comment-reply时,捕捉 trap "" PIPE 并不好:因为SIGPIPE取代了地址空间的当前占用者,操作系统会重置所有捕获信号到其默认处置(exec为“信号死”)。但是,忽略的信号仍然被忽略。

根据您的计划,这可能会更糟或更糟。例如,当SIGPIPE死于cat时,shell会保持沉默,但当SIGPIPE看到cat失败且write时,它会抱怨:

EPIPE

(这是在FreeBSD上;不同的操作系统略有不同,具体取决于他们的工具是多么小心和聪明)。