我该如何修复“管道破裂”错误?

我该如何修复“管道破裂”错误?

我最近重新安装了 RVM(按照http://rvm.io) 在全新安装 Ubuntu 12.10 后,我得到了一个 SSD 驱动器。

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

我收到以下错误:

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

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

rvm is a function

看起来一切都正常?发生了什么事?我该怎么做才能解决这个问题?这种情况并不总是发生。它似乎更不定期发生。我试图找到某种模式,但还没有找到。

答案1

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

当你运行 时type rvm | head -1,bashtype 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:”,因为这type是 bash 本身的内置命令。)

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

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

不过,这并不是什么值得担心的事情,并且它与您的rvm安装没有任何关系。

答案2

你可以修理破损的管道以另一个过程为代价像这样插入tail -n +1管道:

输入 rvm | tail -n +1 | head -1

指示打印输入的第一行和随后的所有内容。输出将与没有输出时完全相同,但程序足够聪明+1,可以检查标准输出并干净地关闭管道。不再tailtail -n +1管道破裂

答案3

让我们尝试一下yes,无休止的打印过程是的……

以前,yes当达到限制时,进程会被 SIGPIPE 杀死。

➜ set -o pipefail
➜ yes | head -n 1
y
➜ echo $?        
141

我的解决方案

➜ yes | (head -n 1;dd status=none of=/dev/null)
y


# the process will still running and output to null

您可以yes用您的程序替换。

答案4

我们可以用来awk替换head,而不是过早退出。(这应该适用于所有 awk 实现的变体):

type rvm | awk 'NR==1'

或者更一般地,对于前 N 行:

# First 10 lines:
type rvm | awk -v N=10 'NR<=N'

解释一下 awk 脚本:NR==1是条件,当满足时,应采取行动。NR是当前记录数的 awk 变量。 操作通常在 中的条件之后给出{...}。 当省略操作时,默认打印整行{print}。 所以awk 'NR==1'意味着打印“记录数”为 1 的行,即第一行。

类似地,在 中awk -v N=10 'NR<=N'-v N=10表示N=10作为变量传递给以下 awk 脚本。 和'NR<=N'表示打印“记录数”小于/等于 的行N

为什么这不会触发管道损坏错误?这是因为在上面的 awk 脚本中,它会处理每一行直到文件末尾,因此它不会过早关闭管道的读取端。

一些性能注意事项:

  1. 我们不会缓存这些行并将它们打印出来END,因此从内存角度来看,处理大量行是可以的
  2. 由于我们在这里不引用字段(例如,,,$1... $2),所以不应该发生字段拆分,这进一步节省了潜在的长行和大量行的计算拆分。
    • (根据 POSIX 文档[1]:“在评估记录中字段的第一次引用之前,应根据正则表达式中的规则,使用读取记录时当前的 FS 值,将记录拆分为字段”)。

最后,奇怪的是,如果我们确实想模拟head提前退出的行为(以及因此出现的管道破裂症状),我们可以添加exit语句:

yes | awk -v N=10 'NR<=N; NR>N {exit}'  # print 10 "yes"

[1]:POSIX awk 手册

相关内容