为什么 hexdump 尝试通读 EOF?

Căc*_*rău 5 linux io hexdump

当从终端中运行,hexdump并没有一个单一的反应^D在该行的开头,如catodbc等做,除非没有输入尚未:

prompt% hexdump -C
<control-D>
prompt% hexdump -C
hello
<control-D><control-D> # a single ^D won't do
00000000  68 65 6c 6c 6f 0a                                 |hello.|
00000006
Run Code Online (Sandbox Code Playgroud)

我可以在 fedora 28 和 debian 9 上重现这个。尽管在 Debianhexdump上提供bsdmainutils,但在 bsd 上不会发生。

这只是一个错误吗?是否有其他已知的程序会表现出这种行为?

小智 3

感谢@JdeBP 的提示我能够创建一个小型测试用例,其功能与以下内容相同hexdump

#include <stdio.h>

int main(void){
        char buf[64]; size_t r;
        for(;;){
                printf("eof=%d, error=%d\n", feof(stdin), ferror(stdin));
                r = fread(buf, 1, sizeof buf, stdin);
                printf("read %zd bytes, eof=%d, error=%d\n",
                        r, feof(stdin), ferror(stdin));
                if(!r) return 0;
        }
}
Run Code Online (Sandbox Code Playgroud)

当在基于 glibc 的系统(典型的 Linux 桌面)上运行时。

prompt$ ./fread-test
eof=0, error=0
<control-D>
read 0 bytes, eof=1, error=0

prompt$ ./fread-test
eof=0, error=0
hello
<control-D>
read 6 bytes, eof=1, error=0
eof=1, error=0
<control-D>
read 0 bytes, eof=1, error=0
Run Code Online (Sandbox Code Playgroud)

当在 bsd、solaris、busybox (uclibc)、android 等上运行时:

prompt$ ./fread-test
eof=0, error=0
hello
<control-D>
read 6 bytes, eof=1, error=0
eof=1, error=0
read 0 bytes, eof=1, error=0
Run Code Online (Sandbox Code Playgroud)

根据我对标准的不专业解释,这看起来像是glibc(GNU C 库)中的一个错误。

关于fread

对于每个对象,应对 fgetc() 函数进行 size 调用,并将结果按读取顺序存储在恰好覆盖该对象的无符号字符数组中。

关于fgetc

如果流指向的输入流的文件结束指示符未设置并且存在下一个字节,则 fgetc() 函数应获取下一个字节

看起来,即使设置了 eof 指示符,glibc 也会尝试“获取下一个字节”。

事实上,它实际上是GNU C 库中的一个错误,在 BSD 或 musl C 库中不存在。 此事早在2005年就已为人所知。Ulrich Drepper 在 2007 年关闭了 bug 报告,但没有修复该 bug。 它在 2012 年进行了讨论,其中指出其他 C 库没有也没有这种行为,1999 年的 C 标准对此非常具体,并且 Solaris甚至有一个特殊的机制,当c99用作编译器而不是cc.

终于在2018年修复了。GNU C 库 2.28 版中已修复该问题。Debian 当前的“稳定”版本(第 9 版)位于 GNU C 库的 2.24 版上,因此,在报告 14 年后,该错误仍在继续显现。

正如 GNU C 库讨论中所指出的,编写的软件可能需要GNU C 库的怪癖,而不考虑其他 C 库(例如 musl)或其他平台上的行为。然而,在上述多年来的讨论中,并没有确定这样的计划。鉴于一些程序已被旧的 GNU C 库破坏,要求用户连续两次发出 EOF 信号;包括hexdump这里patch2018 年 StackOverflow 上的其他内容。