如何解决管道破裂的错误?


36

在获得SSD驱动器后,在全新安装Ubuntu 12.10之后,我最近重新安装了RVM(按照http://rvm.io上的说明)。

现在,当我键入: type rvm | head -1

我收到以下错误:

rvm is a function
-bash: type: write error: Broken pipe

但是,如果我立即重复该命令,那么我只会收到:

rvm is a function

看来一切都还好吗?发生了什么?我该如何解决?它并不总是发生。它似乎是零星的。我已经尝试找到某种模式,但是还没有。

Answers:


57

在这种情况下很少见到“破损的管道”,但这很正常。

当您运行时type rvm | head -1,bash type rvm在一个进程head -1中执行,在另一个进程中执行。1的标准输出type连接到管道的“写入”端,的标准输入head连接到“读取”端。这两个过程同时运行。

head -1过程从stdin读取数据(通常以8 kB为块),打印出一行(根据-1选项),然后退出,从而导致管道的“读取”端关闭。由于该rvm功能相当长(由bash解析和重建后约为11 kB),因此这意味着head退出时type仍需要写入几kB的数据。

此时,由于type试图写入另一端已关闭的管道(损坏的管道),因此它校准的write()函数将返回EPIPE错误,翻译为“损坏的管道”。除了此错误外,内核还将SIGPIPE信号发送给type,默认情况下,该信号立即终止进程。

(该信号在交互式Shell中非常有用,因为大多数用户不希望第一个进程继续运行并试图写到任何地方。同时,非交互式服务会忽略SIGPIPE –对于长时间运行的守护程序而言,这样做不利。死于如此简单的错误-因此他们发现错误代码非常有用。)

但是,信号传递不是立即100%进行的,在某些情况下,write()返回EPIPE,并且该过程在接收到信号之前会继续运行一会儿。在这种情况下,type在被SIGPIPE杀死之前,有足够的时间来注意到写入失败,翻译错误代码甚至将错误消息打印到stderr。(错误消息显示为“ -bash:类型:”,因为它type是bash本身的内置命令。)

这在多CPU系统上似乎更常见,因为type进程和内核的信号传递代码实际上可以同时在不同的内核上运行。

可以通过修补type内置消息(在bash的源代码中)以在收到来自write()函数的EPIPE时立即退出的消息来删除此消息。

但是,没有什么可担心的,它与您的rvm安装没有任何关系。


谢谢!我很担心 昨晚我花了一个小时进行一次Google搜索,以对rvm安装进行故障排除并对其进行修复以尝试修复它。我只是换了一个SSD驱动器,并使用LVM并对硬盘驱动器进行了加密,所以有很多变量在起作用,我不确定什么可能会横盘整理。谢谢您放心!
杰森·舒尔茨

多年来,我一直在ls通过管道输送输出head -1,而今天,我收到了一条断开的管道消息。
图兰斯·科尔多瓦

1
(注意:“管道破损”错误并非来自信号。它来自errno。虽然shell可能会显示用于信号诱导退出的文本消息,但通常足够聪明以至于假装SIGPIPE出口是“干净的”。)
grawity

23

您可以通过在管道中插入来修复损坏的管道,而以另一个过程为代价tail -n +1,如下所示:

输入rvm | 尾-n +1 | 头-1

+1通知tail打印输入和遵循事物的第一道防线。输出将与tail -n +1不存在的输出完全相同,但是该程序足够智能,可以检查标准输出并干净地关闭管道。不再有破损的管道


1
好招 我使用的情况不同于此处提供的情况。谢谢!
有人仍在使用您的MS-DOS

6
看起来是一个很好的解决方案,但是在带有尾巴8.21的Ubuntu 14.04.2上,我得到“尾巴:写入错误:管道损坏”,这没有任何改善。
罗杰·迪克

2
@RogerDueck是正确的。我还可以在Mandriva系统上看到类似问题的find /var/lib/mysql -xdev -type f -daystart -mmin +5 -print0 | xargs -0 ls -ldt | tail -n +1 | head可靠输出xargs: ls: terminated by signal 13。众所周知,问题是输入耗尽的原因之一,实际上只有一个命令可以处理缓冲:dd。添加| dd obs=1M到管道中可以修复我的用例SIGPIPE。
Andrew Beals 2015年

3
我将进一步修改我的建议,尽管我会指出,我认为xargs或type不应引起SIGPIPE的困扰,这是:type rvm | (head -1 ; dd of=/dev/null) 当然,这与其他建议类似,因为它会导致所有输入都得到处理,但它dd应该是处理此类问题的最有效程序。
安德鲁·比尔斯

3
有关SIGPIPE违规者的评论,请访问:mail-index.netbsd.org/tech-userlevel/2013/01/07/msg007110.html
Andrew Beals,

2

write error: Broken pipe消息涉及一个写入过程,该过程试图写入一个管道,而该管道的读取端上没有任何读取器,并且在特殊情况下,SIGPIPE信号被设置为被当前进程或父进程忽略。如果设置SIGPIPE为忽略的是父进程,则子进程不可能在非交互shell中再次撤消该操作。

不过,可能杀死type rvm时,head -1通过使用明确的子shell终止。这样,我们可以将background type rvm,发送typepidhead -1subshel​​l,然后在EXIT该处实现陷阱以type rvm明确杀死它。

trap "" PIPE        # parent process sets SIGPIPE to be ignored
bash                # start child process
export LANG=C
# create a fake rvm function
eval "
rvm() {
$(printf 'echo line of rvm code %s\n' {1..10000})
}
"

# rvm is a function
# bash: type: write error: Broken pipe
type rvm | head -1

# kill type rvm when head -1 terminates
# sleep 0: do nothing but with external command
( (sleep 0; type rvm) & echo ${!} ; wait ${!} ) | 
    (trap 'trap - EXIT; kill "$typepid"; exit' EXIT; typepid="$(head -1)"; head -1)

从grawity的答案中:type在被SIGPIPE杀死之前,有足够的时间来注意到写入失败,翻译错误代码甚至将错误消息打印到stderr。我认为您的解决方案不会阻止生产者进程(type在此)对失败的写入做出反应(由于封闭的管道),对吗?
Piotr Dobrogost
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.