我读过很多“如何将 iso 写入 USB”的问题。我正在寻找一种逆转这个过程的方法。我的问题是关于使用包含 Windows 安装程序或 Mac 安装程序的 USB 棒,并使用它dd
来仅读取 USB 中重新创建原始 iso 所需的部分。
场景:
我有一个 Windows 10 2020 年 10 月版 USB,是我在 11 月刻录的。我想使用 将其备份到磁盘dd
。类似以下内容:
dd if=/dev/sda of=backup-windows.iso bs=4m
我希望能够将其与原始 iso 进行比较并获得匹配。换句话说,当 ISO 只有 7-8GB 时,我不想制作 64 GB 的 USB 驱动器映像。
答案1
初步说明
本答案介绍了在符合 POSIX 或几乎符合 POSIX 的操作系统中有效的命令,特别是在 Linux 或 macOS 中。这些命令不适用于纯 Windows(但我认为可以调整答案以在 WSL 或 Cygwin 中工作)。
分析
好的,您已将一些图像复制到 USB 驱动器/dev/sdx
。通常,您无法仅根据驱动器的内容和属性来完全逆转该过程,因为您不知道要读取多少字节。上限是驱动器的大小(在您的情况下为 64 GB),其他一切都是猜测。驱动器包含字节流,并且没有标记说“在此处停止读取以获取原始图像”。
通过分析分区表(如果有的话),可以进行有益的猜测。但即便如此,您也无法确定原始映像是否更大(末尾有垃圾或非垃圾)。
但是,如果你想将结果与其他图像进行比较,我们就称其为the_other_file
。我猜你希望这两幅图像是相同的。要相同,它们必须具有相同的大小。这意味着你知道要读取多少个字节/dev/sdx
:确切的字节数the_other_file
。你可以通过调用来获取这个数字
wc -c /path/to/the_other_file
或者
</path/to/the_other_file wc -c
注意前者是独立命令,而后者需要 shell(来处理<
)。从现在开始我假设你在 shell 中。
复印
如果你的最终目标是比较,那么你不需要复制到常规文件。在决定之前,请阅读整个答案。明确的问题是:
我怎样才能使用从 USB 重新创建安装程序 ISO
dd
?
它似乎dd
是从文件读取精确字节数的唯一 POSIX 方法。此方法需要ibs=1
,可能会比较慢。要读取N
字节:
dd ibs=1 count=N if=/dev/sdx of=image_file
生成的文件是image_file
。
原始映像是设计为写入块设备的图像,其大小很可能是 512 字节的倍数,也可能是 4096 字节的倍数。ibs
那么您可能可以使用更大的值。
如果N
是精确的ibs
您想要使用的某个较大值的倍数,那么您可以使用ibs
经过count
调整的较大值。后面的数字指定要读取的count=
大小的块数。注意:ibs
bs
指定ibs
和obs
(参见man 1 dd
)。bs=4m
(从你的例子来看)既不属于也不bs=4M
属于POSIX 规范dd
。有效后缀为k
和b
。您可能会惊讶于规范还允许采用 形式的字符串AxB
。要获得 4 MiB,请使用bs=4kx1k
;要获得 4 MB,请使用bs=4000000
或bs=4000x1000
等。- 总的来说,Keep Mind
dd
是一个难以正确使用的工具。它存在一些缺陷。请参阅这和这。
如果N
不是ibs
您要使用的某个较大值的精确倍数,则可以使用较大值读取您要读取的大部分内容ibs
,并在使用 读取时附加缺失部分ibs=1
。这很麻烦,需要一些计算,但从技术上讲仍然是可行的;尽管几乎没有用处。如果您不限于 POSIX,还有更好的方法。
GNUdd
支持无论 是什么iflag=count_bytes
都会计数字节。此外,它还提供了一个不错的工具来读取精确的字节数。对于您的情况,命令可能是:count
ibs
iflag=fullblock
dd
dd bs=4M iflag=fullblock,count_bytes count=N if=/dev/sdx of=image_file
或(避免手动输入N
):
dd bs=4M iflag=fullblock,count_bytes count="$(</path/to/the_other_file wc -c)" if=/dev/sdx of=image_file
如果我是你我会使用head -c
:
head -c "$(</path/to/the_other_file wc -c)" /dev/sdx >image_file
注意wc -c
是可移植的(由 POSIX 指定)但head -c
不是(尽管我相信head
macOS 支持-c
)。
比较
您可以直接比较,而无需创建任何image_file
。以下命令是可移植的:
cmp /dev/sdx /path/to/the_other_file
在您的情况下/dev/sdx
,设备大于the_other_file
。您将收到一条消息(到 stdout)文件不同,或一条消息(到 stderr)EOF on the_other_file
。在这两种情况下,退出状态都是1
。后一条消息意味着 的开头/dev/sdx
与 相同。这是复制到the_other_file
后您所期望的结果。the_other_file
/dev/sdx
如果您想通过退出状态进行判断(在脚本中很有用),那么您需要确保您正在比较大小相同的文件。这仍然可以在不创建任何文件的情况下实现image_file
:
head -c "$(</path/to/the_other_file wc -c)" /dev/sdx | cmp - /path/to/the_other_file
(管道的退出状态来自最后一个命令;此处:来自cmp
。)注意head -c
(仍然)不可移植。如果需要,:)
您可以将其替换为可移植的:dd …
dd ibs=1 count="$(</path/to/the_other_file wc -c)" if=/dev/sdx | cmp - /path/to/the_other_file
image_file
或者,如上所示创建后,您可以直接image_file
比较the_other_file
:
cmp image_file the_other_file
退出状态0
表示文件相同;退出状态1
表示文件不相同。
在交互工作时,您可以关注输出(我的意思是 stdout + stderr,即您通常在终端中看到的所有输出)。 的空输出cmp
表示文件相同; 的非空输出cmp
表示它所说的内容。 注意dd
(但不是head
)即使没有错误也会打印到 stderr。 我故意写了“空输出从cmp
",而不仅仅是“空输出”。如有疑问,无论如何都要检查退出状态:echo $?
紧接着运行cmp
(或以 结尾的管道之后运行cmp
)。