tee iso 内容写入 dd 或直接写入 /dev/sda?

Aar*_*all 6 iso dd system-installation tee

一年多前,我撰写了有关如何在 Linode 上安装 NixOS 的教程

似乎大多数涉及将 iso 文件放在安装介质上的教程总是使用dd,这就是为什么“现在 dd 仍然相关吗?” 没有回答我的问题。本教程最初也使用过dd,我对其进行了稍微修改,以获取 sha256 校验和来验证 iso,同时使用 管道从源中传输它tee,如下所示:

curl -L $iso | tee >(dd of=/dev/sda) | sha256sum
Run Code Online (Sandbox Code Playgroud)

但在我看来,dd这里有点多余,而且实际上比下面的慢很多:

curl -L $iso | tee /dev/sda | sha256sum
Run Code Online (Sandbox Code Playgroud)

几个月前,在帮助别人遵循教程时,我记得他们在使用更简单的方法时遇到了问题,但是当我上周末刚刚尝试过两次单独的安装时,它似乎工作得很好,而且速度也相当快。

此修改是否足够可靠,可以提交更新教程的拉取请求?

或者我只是幸运地让它工作,并且dd实际上用于创建安装介质更安全、更可靠 - 因此我们应该按原样保留教程?

Gil*_*il' 15

使用dd并不更安全、更快或更不可靠。事实上,这里引入了两种额外的失败风险。如果人们手动遵循这些说明,这两种风险在实践中都不太可能成为问题,但如果这些说明位于自动化脚本中,则这两种风险都将是严重的错误。

\n

Bug:竞争条件

\n

观察:

\n
bash-5.0$ echo hello | tee >(sleep 1; echo done); echo next step\nhello\nnext step\nbash-5.0$ done\n
Run Code Online (Sandbox Code Playgroud)\n

在 bash 中,输出过程替换是异步的。当命令包含进程替换时>(\xe2\x80\xa6),它不会等待进程替换完成。

\n

因此,当\xe2\x80\xa6 | tee >(dd of=/dev/sda) | sha256sum返回时,可能还有数据仍在通过dd. 这不太可能持续足够长的时间以供人类做出反应并键入另一个命令,但它可能会破坏运行其他命令(例如ejectmount之后)的脚本。

\n

Bug:缺少错误检测

\n

让我们开始一个一切正常的名义情况。

\n
bash-5.0$ head -c 1m </dev/zero | tee >(cat >/dev/null) | wc -c; echo $?\n1048576\n0\n
Run Code Online (Sandbox Code Playgroud)\n

现在让我们看看如果数据写入命令失败会发生什么情况。

\n
bash-5.0$ head -c 1m </dev/zero | tee >(false) | wc -c; echo $?\n8192\n0\n
Run Code Online (Sandbox Code Playgroud)\n

该命令具有成功状态,因为管道的退出状态仅取决于右侧。这个想法是,如果将数据生成器通过管道传输到数据处理器,则数据处理器的工作就是检测故障。不幸的是,这仅适用于数据格式允许数据处理器检测故障的情况,但通常情况并非如此,特别是此处并非如此。

\n

请注意,tee一旦无法写入连接到的管道,就彻底放弃false。由于false从未读取任何数据,因此通过 to 的唯一数据wc -c是两个PIPE_BUF(一个tee写入两个管道,另一个tee仅写入管道 towc但未能写入管道 to false)。根据false退出与tee写入管道和wc消耗数据的相对时间,可能只有 1 个或 0 个PIPE_BUF通过。

\n

tee通过设置该选项可以检测故障pipefail。(这种可能性存在于 ksh、bash 和 zsh 中,但不存在于普通 sh 中。)

\n
bash-5.0$ set -o pipefail; head -c 1m </dev/zero | tee >(false) | wc -c; echo $?\n8192\n141\n
Run Code Online (Sandbox Code Playgroud)\n

tee无法写入管道,因此它死于 SIGPIPE,对应的 shell 状态为 128 + SIGPIPE 的数值(在 Linux 上为 13)。由于该pipefail选项,这会导致整个管道以相同的状态退出。

\n

请注意,管道反映的是 的失败tee,而不是直接反映进程替换中命令的失败。如果进程替换中的命令成功读取了所有数据但未成功处理,则不会检测到错误。

\n
bash-5.0$ head -c 1m </dev/zero | tee >(cat >/dev/null; false) | wc -c; echo $?\n1048576\n0\n
Run Code Online (Sandbox Code Playgroud)\n

wc -c处理了所有数据。cat >/dev/null; false模拟未正确处理所有输入的命令。尽管如此,命令的状态表明成功。

\n

在您的实际示例中,这意味着如果数据末尾存在错误,例如因为目标设备比图像小一点,则不会检测到此错误(除非通过错误消息来自dd)。

\n

简单、正确的解决方案

\n
set -o pipefail\ncurl -L $iso | tee /dev/sda | sha256sum\n
Run Code Online (Sandbox Code Playgroud)\n

或者,可以说更简单:

\n
curl -L $iso | tee >/dev/sda >(sha256sum)\n
Run Code Online (Sandbox Code Playgroud)\n

请注意,如果没有pipefail,第二个命令即使curl失败也会成功。然而,这种失败肯定会导致错误的校验和。

\n

关于 dd 使用的一般说明

\n
\n

似乎大多数涉及将 iso 文件放在安装介质上的教程总是使用 dd,这就是为什么“现在 dd 仍然相关吗?” 没有回答我的问题

\n
\n

嗯,或多或少确实如此。具体来说,它回答了是否有任何目的的问题dd:没有。它没有涵盖使用dd这种特定方式时出现的具体问题,这一次实际上并不是由于dd其本身造成的。

\n

大多数教程使用的原因dd是大多数教程使用dd. 这是一个自我延续的传奇。人们使用它dd是因为他们在其他地方看到过它的使用,尽管他们并不真正理解为什么。它的语法与其他命令不同,因此显得有些神秘和强大。但在dd of=/dev/sda,所有的力量都在/dev/sda,却没有dd。这只是一种自命不凡、脆弱的写作方式cat >/dev/sda

\n