$> set -o pipefail
$> fortune | head -1 > /dev/null && echo "Повезло!" || echo "Вы проиграли"
Повезло!
$> fortune | head -1 > /dev/null && echo "Повезло!" || echo "Вы проиграли"
Вы проиграли
这是 fortune
条件程序无 exit(rand())
.
你可以解释吗 这里出了什么问题?
抒情历史题外话
我第一次认识这个 Heisenbug 是在二十五年前。 然后,对于 FaxNET 中的网关,需要通过以下方式创建多个实用程序
我之前处理 sendmail 和 uucp/uupc 中的错误的经验增加了我在“彻底的错误处理”方面的努力。 深入研究这个故事的细节是没有意义的,但我在这个 Heisenbug 上挣扎了两周 10-14 个小时。 于是,就记住了,昨天这位老熟人又来拜访了。
TL;DR 答案
效用 head
может 关闭通道 fortune
立刻 当他读到第一行时。 如果 fortune
输出多一行,则对应调用 write()
将返回错误或报告输出的字节数少于请求的字节数。 反过来,用仔细的错误处理来编写 fortune
有权在其退出状态中反映这种情况。 然后由于安装 set -o pipefail
将工作 || echo "Вы проиграли"
.
但是, head
可能无法及时赶到 之前关闭 fortune
将完成数据输出。 然后就可以了 && echo "Повезло!"
.
在我的今天之一 GNUMakefile
echo '#define MDBX_BUILD_COMPILER "$(shell set -o pipefail; $(CC) --version | head -1 || echo 'Please use GCC or CLANG compatible compiler')"'
翻译成人类
这里常见的是 --version
它询问他是谁,如果不支持该选项,则会插入一个存根 “请使用 GCC 或 CLANG 兼容编译器”.
像
#define MDBX_BUILD_COMPILER "lcc:1.23.20:Sep--4-2019:e2k-v3-linux Please use GCC or CLANG compatible compiler"
坦白说,我并没有立即认出我的老“熟人”。 此外,该项目已经在 Elbrus 和许多不同的发行版(包括 Alt)下进行了多次测试。 具有各种编译器、GNU Make 和 bash 版本。 因此,我不想在这里看到我的错误。
当试图重现问题和/或了解发生了什么时,更多奇怪的事情开始发生。
命令行拼写:
echo "#define MDBX_BUILD_COMPILER '$(set -o pipefail; LC_ALL=C cc --version | head -1 || echo "Please use GCC or CLANG compatible compiler")'"
它时不时地会产生额外的文本,然后就不会了……通常其中一个选项会保留相当长的时间,但如果你戳得更久,你总是会得到两个!
当然, strace
顺便说一句,就像任何有自尊心的人一样 strace
宁愿不复制。
发生什么了?
- 效用
head
一旦读取了请求的行数,就有权利(或者更确切地说,甚至被迫)关闭正在读取的通道。 - 数据生成程序编写者(在本例中
cc
) может 打印多行并 自由的 通过多次调用来做到这一点write()
. - 如果 在写入端记录结束之前,读取端有时间关闭自己端的通道,然后写入端会收到错误。
- 作家计划 有权利 两者都会忽略通道写入错误并将其反映在完成代码中。
- 由于安装原因
set -o pipefail
如果至少一个元素的结果非零,则管道完成代码将非零(错误),然后它将起作用|| echo "Please use GCC or CLANG compatible compiler"
.
根据编写程序如何处理信号,可能会有所不同。 例如,程序可能异常终止(自动生成非零/错误终止状态),或者 write()
将返回写入的字节数少于请求和设置的字节数的结果 errno = EPIPE
.
是谁的错?
在所描述的情况下 一切都一点点。 错误处理在 cc
(lcc:1.23.20:Sep—4-2019:e2k-v3-linux) 不是 多余的。 在许多情况下,最好还是谨慎行事,尽管这确实会暴露出为传统行为设计的样板文件中突然存在的缺陷。
怎么办?
错误:
fortune | head -1 && echo "Повезло, но вы рискуете!" || echo "WTF?"
更正:
-
(fortune && echo "Успешно" || echo "Ошибка") | head -1
在这里,在服务嵌套管道(括号“内”)时,命令解释器将处理管道的提前关闭。 因此,如果
fortune
会在status中报写入关闭通道错误,然后输出|| echo "Ошибка"
它不会到达任何地方,因为通道已经关闭。 -
fortune | cat - | head -1 && echo "Успешно" || echo "Ошибка"
这是实用程序
cat
充当阻尼器,因为它忽略了误差EPIPE
撤回后。 现在就可以得出结论了fortune
小(几行)并且适合通道缓冲区(从 512 字节到 ≈64K,在大多数操作系统中≥4K)。 否则问题可能会再次出现。
如何正确处理 EPIPE
以及其他录音错误?
没有单一正确的解决方案,但有一些简单的建议:
EPIPE
需要 必须处理 (并反映在退出状态中)当输出需要完整性的数据时。 例如,在归档程序或备份实用程序的操作期间。EPIPE
最好忽略 显示信息和辅助消息时。 例如,当显示选项信息时--help
или--version
.- 如果正在开发的代码之前可以在管道中使用
| head
,然后EPIPE
最好忽略,否则最好处理并反映在退出状态中。
我想借此机会向团队表示感谢
继续努力 同志们,加油
谢谢
KDPV 来自
来源: habr.com