grep: writing output: Broken pipe in iTerm2
前天用 iTerm2,在执行一个 grep "xxx" filename | head (filename 这个文件应该相当大,grep 到的内容也应该有很多)这样的命令时,遇到大量如下错误输出:
grep: writing output: Broken pipe
而在 Mac 自带的 Terminal.app 里面执行完全一样的命令,不会有任何错误。用 which 查看,使用的确实是同一个 grep 命令,同一个 head 命令。再仔细观察,发现其实 iTerm 里面,命令也输出了正确的结果,那后面的这些错误信息应该是输出到 stderr 的。把命令改成
grep "xxx" filename 2>errors | head
确实,错误信息都跑到 errors 文件里了。这时又发现,在 Terminal.app 里命令在输出10行之后立即结束执行,而在 iTerm 里则取决于这个文件有多大,grep 出来的内容有多少行,它就会执行相应长的时间。网上看到有人说这是 head 取得10行后立即退出,这个 pipe 的读端就没了,grep 继续往 pipe 写,于是 - broken pipe, 只要将错误定向到 /dev/null, 忽略即可。可是我经常用 grep 来在特别大的日志文件里找东西,进行下一步分析前,就先用 head 看看 grep 的正则表达式写的对不对。每次 grep 都不管 head 只读少数行这个现实,一直执行到读完整个文件,太浪费时间和资源了。
为什么在 Terminal 里,grep 会在 head 退出之后马上退出呢?当进程往一个 broken pipe 写东西的时候,会收到一个 SIGPIPE 信号,导致它退出。这时就觉得,肯定是 iTerm2 做了什么手脚,导致 SIGPIPE 这个信号没有被 grep 收到。
用一个 dtrace 命令来看系统里信号的发送接收情况 (参考 dtrace oneliners):
sudo dtrace -qn \'proc:::signal-send { printf("%s (PID=%d) sent signal %d to PID %d\\n", execname, pid, arg1, args[1]->pr_pid) } proc:::signal-handle { printf("%s (PID=%d) was sent signal %d\\n", execname, pid, arg0)}\'
在Terminal.app 里执行上面的命令,可以跟踪到:
grep (PID=3524) sent signal 336414016 to PID 3524 grep (PID=3524) was sent signal 13
而在 iTerm 里执行则什么都没有(应该只能看见时间同步服务 ntpd 不停接收信号)。从上面也看到,信号其实是 grep 自己发给自己的,13 就是 SIGPIPE。我不了解 Unix 编程,猜想收到 SIGPIPE 退出这个应该是 libc 这样底层的库已经定义的行为吧。
找到 iTerm2 的源代码,竟然轻易在它的 main.m 中找到了可疑代码:
int main(int argc, const char *argv[]) { signal(SIGPIPE, SIG_IGN); sigset_t signals; sigemptyset(&signals); sigaddset(&signals, SIGPIPE); sigprocmask(SIG_BLOCK, &signals, NULL); return NSApplicationMain(argc, argv); }
一开始我只注意到第一行,signal(SIGPIPE, SIG_IGN); 即在收到 SIGPIPE 信号时忽略,但是注释掉之后发现和原来一样的行为 (如果只是 SIG_IGN, dtrace 应该能跟踪到发送)。接着注意到下面几行也是处理 SIGPIPE 信号的,把 SIGPIPE 完全 block 掉!至少 sigprocmask 这个函数是对子进程有作用的,因为在 iTerm2 里执行 grep,grep 就是 iTerm2 的子进程(具体关系是 iTerm2 - login - bash - grep,可以安装个 pstree 查看)。
真相大白,果然把相关代码全部注释掉之后,用 XCode 自己 build 了一个,这个问题解决掉了。git blame main.m,发现这段代码是 2011-10-31 在 474cd19a972cea6a0d34f552a8a7ecaec514f3f9 这个 commit 加入的。所以在 iTerm2 下载列表里,使用 20111020 那个 build 应该没这个问题,而我用的是新一点的 20111219.
在 google code 上创建了一个 issue, George Nachman 很快 fix 了这个问题。显然我的改法太简单粗暴,具体修改见 commit b0a6289bc0. 没想到竟然之前有人汇报的一个 issue 也是这个 bug 引起的,作者在此 issue 中上传了一个最新的 build 版本,大家可以使用。
建议继续学习:
- Linux grep命令用法 (阅读:6059)
- grep 正则表达式选项要记得转义 (阅读:5186)
- 学习Grep,Sed中的正则 (阅读:3965)
- grep 命令的buffer选项 (阅读:3114)
- 小心grep 的buffer (阅读:3069)
- 利用for + grep awk 解决grep + xargs (阅读:2666)
- jquery中的数组过滤筛选-$.grep() (阅读:2641)
- 在Linux下搜索包含特定字符串的文件列表 (阅读:1670)
- 误删大文件的一个可能解救办法 (阅读:1657)
- grep awk 之buffer问题 (阅读:1476)
扫一扫订阅我的微信号:IT技术博客大学习
- 作者:qingbo.blog 来源: qingbo.blog
- 标签: grep
- 发布时间:2012-01-03 23:16:25
- [68] Twitter/微博客的学习摘要
- [66] IOS安全–浅谈关于IOS加固的几种方法
- [64] android 开发入门
- [64] 如何拿下简短的域名
- [62] find命令的一点注意事项
- [61] Go Reflect 性能
- [60] 流程管理与用户研究
- [59] Oracle MTS模式下 进程地址与会话信
- [58] 图书馆的世界纪录
- [56] 读书笔记-壹百度:百度十年千倍的29条法则