如果您在名为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)
答案 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上;不同的操作系统略有不同,具体取决于他们的工具是多么小心和聪明)。