git - 自定义 Git 命令在执行 git diff 后死于信号 13 (SIGPIPE)

标签 git unix process signals exec

如果您在 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/

相关文章:

git - 我如何压缩 git 中提交日期不是过去的提交?

c++ - 如何在主进程和线程之间使用 Unix 管道?

linux - Bash 脚本问题

java - Java Process.exitValue() 中的值是什么意思?

c# - 如何在不链接的情况下从 .NET 使用命令行 Matlab?

git - 从 git 历史记录中删除已删除的文件

git - 我怎样才能用 Git 只提交删除?

git - 如何将单个提交归因于多个开发人员?

linux - AWK 报告重复行和计数,程序说明

process - 优先抢占式调度