Chinaunix首页 | 论坛 | 博客
  • 博客访问: 1454760
  • 博文数量: 263
  • 博客积分: 10851
  • 博客等级: 上将
  • 技术积分: 2627
  • 用 户 组: 普通用户
  • 注册时间: 2008-11-26 22:40
文章分类

全部博文(263)

文章存档

2013年(4)

2012年(25)

2011年(33)

2010年(50)

2009年(138)

2008年(13)

分类: LINUX

2011-04-27 18:27:07

SIGPIPE From Wikipedia
On POSIX-compliant platforms, SIGPIPE is the signal sent to a process when it attempts to write to a pipe without a process connected to the other end. The symbolic constant for SIGPIPE is defined in the header file signal.h. Symbolic signal names are used because signal numbers can vary across platforms.
SIG is a common prefix for signal names. PIPE refers to the Unix pipe.
Unix supports the principle of piping, which allows processes to send data to other processes without the need for creating temporary files. When a pipe is broken, the process writing to it is sent the SIGPIPE signal. The default reaction to this signal for a process is to terminate.
A simple example of piping is the following.
ps -e | head
This command, when run on a Unix-like machine (including Linux), returns a list of processes, limited to ten lines.
ps -e returns a list of all processes (including those of other users).
head selects the first ten lines.
When ps has written ten lines, head has received all it needs and exits. ps will receive a SIGPIPE when it tries to write the remaining lines, causing it to terminate as well: It is no use writing data that no one will use. Hence it is possible that the reading process terminates while writing the data. This will cause SIGPIPE to be sent to the writing process.
One can ignore SIGPIPE (using, for example, the signal system call). In this case, all system calls that would cause SIGPIPE to be sent will return -1 and set errno to EPIPE.

# set -o pipefail
# cat a.txt | head -n 1   (这里a.txt是一个很大的文件)
y
# echo $?
141
# 返回值非0说明这行管道中有命令出错了, 用strace观察一下,就会发现是SIGPIPE的原因:
# strace -o strace.log cat a.txt | head -n 1
strace -o strace.log cat a.txt | head -n 1
y
cat: write error: Broken pipe (这里比较纳闷, 为什么我直接cat a.txt | head -n 1的时候cat不报这个错出来呢?)
查看strace.log可以发现:
write(1, "y\ny\ny\ny\ny\ny\ny\ny\ny\ny\ny\ny\ny\ny\ny\ny\n"..., 4096) = -1 EPIPE (Broken pipe)
--- SIGPIPE (Broken pipe) @ 0 (0) ---

原因:
对于管道:command1 | command2
当command2进程已经终止,当command1试图往管道中写入时,才会接收到SIGPIPE,SIGPIPE是由管道发送的,而不是由command2进程发送的。因此,命令 " yes | true" 也会立马结束。
参考:
另外有些命令不会处理SIGPIPE信号,当它们接收到SIGPIPE信号时会终止,就像cat一样。
其它很多命令都会忽略SIGPIPE信号,如nl,od,tail,split,wc,sum,sort,cut,sleep等都不会因为接收到SIGPIPE而退出。
测试命令:
cat /dev/zero | true
head -n 1 a.txt | true
find / | true
sleep 50 | true
如果命令能够立即结束,表示它不处理SIGPIPE信号,会立即终止,否则表示会忽略SIGPIPE信号。

另外:
$ find . -name "*rc" |xargs -i cat {}|head -1
[Desktop]
xargs: cat: terminated by signal 13
错误信息似乎并不太好理解,实际上它的意思是:xargs发现它的子进程cat由于信号13被中止了。由于xargs本身属于循环操作,发现错误之后就停止了循环,这是其一;信号13是在cat试图向一个已关闭的pipe管道中写数据的时候,系统产生的,cat收到之后就停止了。类似于在cat输出的过程中,用户按下ctrl+c的效果。
如何避免这种问题呢?很简单,管道后面使用不会提前关闭管道的程序即可,尤其是结合xargs使用的时候,它发现出错就不继续了。比如要用到head可以这样:
$ cat file |head -1
虽然cat仍然会被signal 13关闭,但bash是不会报错的,所以也只能针对一个文件进行操作,即使是使用了通配符也只能head到第一个文件。如果要加上对文件的遍历,可以用到for:
$for file in .*rc;do cat $file |head -1;done
cat依然会被关闭,但是for不会理会它,继续循环。head也可以直接指定文件名,这样我们就可以抛开cat了:
$find . -name "*rc" |xargs -i head -n1 {}
个人认为这是一种最完美的解决方式,即可以用到find强大的搜索指令,还不会涉及到管道的问题。不过如果文件名没有什么特殊要求,还有一种更简单的方式:
$head -n1 .*rc
在head的参数中直接用通配符指定文件。
阅读(3827) | 评论(1) | 转发(0) |
给主人留下些什么吧!~~

少林功夫好2016-09-27 10:00:16

在使用inotifywait脚本时,也遇到这个问题。不晓得从哪里发出来的信号。以下是strace -p pid看到的内容。
read(3, \"\\2\\0\\0\\0\\4\\0\\0\\0\\0\\0\\0\\0\\20\\0\\0\\0datatime.js\\0\\0\\0\\0\\0\", 65536) = 32
stat(\"/etc/localtime\", {st_mode=S_IFREG|0644, st_size=405, ...}) = 0
write(1, \"23/09/16 12:31 /webapp/source_ce\"..., 103) = -1 EPIPE (Broken pipe)
--- SIGPIPE (Broken pipe) @ 0 (0) ---