从文件中读取确切字节数的 POSIX 方法是什么?

从文件中读取确切字节数的 POSIX 方法是什么?

刚刚遇到这个问题,并从所选答案中学到了很多东西:使用 dd 创建随机数据并获得“部分读取警告”。现在警告后的数据真的是随机的吗?

不幸的是,建议的解决方案head -c不可移植。

对于坚持认为这dd是答案的人,请仔细阅读链接的答案,其中详细解释了为什么dd不能作为答案。另外,请注意这一点:

$ dd bs=1000000 count=10 if=/dev/random of=random
dd: warning: partial read (89 bytes); suggest iflag=fullblock
0+10 records in
0+10 records out
143 bytes (143 B) copied, 99.3918 s, 0.0 kB/s
$ ls -l random ; du -kP random
-rw-rw-r-- 1 me me 143 Apr 22 19:19 random
4       random
$ pwd
/tmp

答案1

不幸的是,操作二进制文件的内容dd几乎是 POSIX 中唯一的工具。尽管文本处理工具的大多数现代实现(catsedawk、 ...)可以操作二进制文件,但这不是 POSIX 所要求的:一些较旧的实现确实会因空字节、未由换行符终止的输入或环境字符中的无效字节序列而阻塞编码。

安全使用是可能的,但很困难dd。我花费大量精力引导人们远离它的原因是,有很多建议dd在它既无用又不安全的情况下进行推广。

问题在于dd它的块概念:它假设调用read返回一个区块;如果read返回较少的数据,您会得到一个部分块,这会抛出诸如skipcountoff 之类的东西。下面的示例说明了该问题,其中dd从传输数据相对较慢的管道中读取数据:

yes hello | while read line; do echo $line; done | dd ibs=4 count=1000 | wc -c

在沼泽标准 Linux(Debian jessie,Linux 内核 3.16,dd来自 GNU coreutils 8.23)上,我得到的字节数变化很大,范围从大约 3000 到几乎 4000。将输入块大小更改为 6 的除数,然后输出始终是 4000 字节,正如人们天真的期望的那样 - 输入dd以 6 字节的突发形式到达,并且只要一个块不跨越多个突发,dd就可以读取完整的块。

这提出了一个解决方案:使用输入块大小 1。无论输入是如何产生的,dd如果输入块大小为 1,则无法读取部分块。(这并不完全明显:dd如果被信号中断,则可以读取大小为 0 的块 - 但如果它被中断通过信号,read系统调用返回 -1。read仅当文件以非阻塞模式打开时,才可能返回 0,在这种情况下,最好不要认为仅read在阻塞模式下执行过。read在文件末尾返回 0。)

dd ibs=1 count="$number_of_bytes"

这种方法的问题在于它可能很慢(但不是慢得惊人:只比head -c我的快速基准测试慢大约 4 倍)。

POSIX 定义了其他读取二进制数据并将其转换为文本格式的工具:uuencode(以历史 uuencode 格式或 Base64 输出),od(输出八进制或十六进制转储)。两者都不太适合手头的任务。uuencode可以通过以下方式撤消uudecode,但是计算输出中的字节数很尴尬,因为每行输出的字节数没有标准化。可以从 获得明确定义的输出od,但不幸的是没有 POSIX 工具可以反过来(可以做到,但只能通过 sh 或 awk 中的慢循环,这违背了这里的目的)。

答案2

较新版本的 GNU 实现dd有一个count_bytesiflag。例如:

cat /dev/zero | dd count=1234 iflag=count_bytes | wc -c

会输出类似的东西

2+1 records in
2+1 records out
1234 bytes (1.2 kB, 1.2 KiB) copied, 0.000161684 s, 7.6 MB/s
1234

答案3

使用的部分目的dd是用户可以选择它使用的块大小。如果dd块大小太大而失败,IMO 用户有责任尝试较小的块大小。我可以要求dd一个街区的 TB,但这并不意味着我会得到它。

如果您想要确切的字节数,这将非常慢,但应该可以工作:

dd bs=1 count=1000000

如果即使是一个块大小也会1导致部分读取,……

相关内容