2016-01-31 28 views
0

后死亡。如果你创建你的PATH一个shell脚本调用git-mydiff包含:定制的Git命令从信号13(SIGPIPE)exec'ing git的差异

#!/bin/bash 
exec git diff 

,并用大量的调用git mydiff在资源库变化的,当你退出寻呼机,它会输出:

error: git-mydiff died of signal 13 

但是,如果直接执行path/to/git-mydiff,不会有错误,当您退出寻呼机。

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

我使用的是:git的版本2.5.4(苹果的Git-61)

+0

一种可能性是忽略SIGPIPE像http://stackoverflow.com/a/22464942/5781248 –

+0

@ J.J.Hakala我试过了,但它似乎没有工作。因为'exec'完全替换了当前正在运行的程序,所以我不认为信号陷阱是继承的。这在这里讨论:http://stackoverflow.com/questions/24111981/how-can-i-achieve-bash-exit-trap-when-exec-ing-another-binary – redtree

回答

1

你的程序,在这种情况下mydiff,和(无论您选择为core.pagerless,或)寻呼机通过连接管。操作系统在读取器必须清理一些内容之前可以写入管道的数据量有一定限制,并且寻呼机在暂停之前不会读取整个管道,因此在某些输出量时,管道已经填满并且您的程序在其系统调用write中被阻止。

如果管道的读端消失(由具有寻呼机退出),两件事情发生在这一点上:操作系统提供了一个SIGPIPE信号到你的程序,操作系统有write系统调用失败,并EPIPE错误。通常情况下,第一个信号会在第二个信号发生之前杀死你的程序,但是如果你的程序要赶上或者忽略SIGPIPE,那么第二个就会发生。

下面是在SIGPIPE一个作业控制壳杀死的处理的例子:

> cat book.pdf | : & 
> 
[1] Broken pipe     cat book.pdf | 
     Done       : 

(顺便:这里内置的结肠命令,这是一个空操作,它是一个剩从,我。想,Mashey外壳,有goto作为一个外部程序)运行此作为一个经常前台进程,顺序是沉默:

> cat book.pdf | : 
> 

这是因为shell不埋怨死了,OF- SIGPIPE过程中,自“死于SIGPIPE”是很正常的。

无论出于何种原因,git的前端对于这个死于SIGPIPE的案件而言更为嘈杂。如果您不使用exec,那么看到的是SIGPIPE。贝壳静静地吸收并干净地离开,git不会抱怨。如果您使用使用exec,则shell将被替换为您的程序,并且git前端命令将看到SIGPIPE处于死亡状态并发出抱怨。

一个显而易见的治疗方法是将壳体留下。另一种方法是让壳忽略(抓不住)SIGPIPE,然后做exec

trap "" PIPE 

正如你noted in a comment-replySIGPIPE没有很好:因为exec替换地址的现任主人空间,操作系统重置所有信号捕获到它们的默认配置(对于SIGPIPE是“信号死亡”)。但是,忽略信号仍然被忽略。

根据您的程序,这可能是坏的或更糟的。例如,当SIGPIPEcat模具外壳是无声的,但是当cat看到write失败,EPIPE它抱怨:

$ cat book.pdf | : 
$ trap "" PIPE 
$ cat book.pdf | : 
cat: stdout: Broken pipe 

(这是FreeBSD上;不同的操作系统略有不同,这取决于它们如何小心和聪明的实用工具是)。

+0

谢谢你的深入解释!如果我在'exec git diff'前面添加'trap'“PIPE'到脚本,当'less'退出时,我仍然看到”信号13死亡“。现在不应该由于'EPIPE'而不是'SIGPIPE'而导致失败? – redtree

+1

它应该,是的(除非它明显地将信号重置为默认值)。可能有趣的是抛出正在生成的系统调用(trace,truss,ktrace,无论操作系统提供什么...)。 – torek