如何安全地将 .gz 文件转换为 .xz 文件

如何安全地将 .gz 文件转换为 .xz 文件

我有一些目前已压缩的大文件,我想对它们进行 xz 压缩。我想设置一个脚本来执行此操作,但要小心不要丢失数据,即我永远不应该删除已压缩的版本,除非 xz 版本确实创建正确。由于这些都是大文件,所以我也不想先将文件解压缩到磁盘。我在想管道set -o pipefail; gzip -dc file.gz | xz > file.xz && rm file.gz可能接近我想要的。正确的做法是什么?这是否保证在删除最终文件之前捕获发生的任何故障?

答案1

添加 SHA1 和(从数学上保证了文件在哈希值匹配时匹配,文件不匹配时哈希值不匹配,达到极高的确定性)增加了数据完整性的衡量标准,以防止磁盘子系统在写入时可能犯(无声)错误的情况。无声损坏很少见,但一旦发生,就会非常危险。

当然,如果读取时出现随机错误,结果仍然可能令人困惑,但在这种情况下,结果总和无论如何都不会匹配,而且非常肯定。换句话说,如果系统损坏(RAM 或磁盘产生错误位/翻转位/损坏的数据),那么这将失败,而简单方法&&可能会成功,并且它到达损坏数据行的几率rm消失地小(因为大多数错误往往以随机的方式破坏数据,所以在读回过程中随机变化导致 SHA1 中发生哈希冲突的可能性非常小。)

#!/bin/bash
set -e
set -o pipefail
ORIGSUM=$(gzip -dc file.gz | tee >(xz > file.xz) | sha1sum)
NEWSUM=$(unxz -c file.xz | sha1sum)
if [ "${ORIGSUM}" = "${NEWSUM}" ]; then rm file.gz; fi

使set -eshell 脚本立即退出任何脚本的行返回非零的退出代码。

然后我们使用tee命令将解压后的文件输出复制到两个都压缩机xzsha1sum程序。sha1sum通过将压缩档案中的原始数据临时解压到 sha1sum 程序中来计算其 SHA1 总和,该程序读取数据以计算总和,然后丢弃数据。通过使用tee,我们只需支付一次解压文件的 CPU 成本。

然后,我们执行额外的计算昂贵的步骤(用于超级额外验证),并剥离文件上的 xz 压缩(临时,放入流中)并将其传送到 sha1sum,以获取我们的“新文件” SHA1 和。

然后我们比较这两个和,如果它们不是相等的字符串,或者其中一个或两个都是零长度,我们要么会得到一个脚本错误(由于 而退出set -e),要么文件不会被删除。else如果您愿意,您可以实现一个用户友好的错误处理子句,但这个基本脚本本身将非常安全,尽管对以交互方式运行命令的用户来说信息量不大。

最终,file.gz只会被解开当且仅当file.gz和的未压缩内容file.xz在计算哈希值时完全相同,并且确定性极高(发生错误的可能性约为 1/1,后面有 300 个零)。此时,您只需要担心数据损坏该脚本退出。;)


表现

该脚本的运行速度与问题中的原始脚本几乎相同,除了对于运行的部分unxz。幸运的是,从 LZMA 解压缩的速度非常快,几乎和常规 Zip 一样快,比压缩至LZMA。如果你有一个快速的 CPU,并且文件足够小,这不应该添加脚本的运行时间很长,但如果您更看重数据完整性而不是性能,那么它显然更胜一筹。


功劳应得者

StackOverflow 上的这个答案对我编写此脚本提供了很大的帮助。

相关内容