我有一些当前已被 gzip 压缩的大文件,我想对它们进行 xz。我想设置一个脚本来执行此操作,但我要小心不要丢失数据,即我永远不应该删除 gzipped 版本,除非 xz 版本确实创建正确。由于这些是大文件,我也不想先将文件解压缩到磁盘。我在想一个管道set -o pipefail; gzip -dc file.gz | xz > file.xz && rm file.gz
可能接近我想要的。这样做的正确方法是什么?这是否保证可以捕获在删除最终文件之前发生的任何故障?
添加 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
Run Code Online (Sandbox Code Playgroud)
在set -e
刚刚尽快使得shell脚本退出任何脚本的行返回一个非零的退出代码。
然后我们用tee
命令将文件的未gzip压缩输出复制到两者的xz
压缩机,并在sha1sum
节目。sha1sum
通过将 gzip 存档中包含的原始数据临时解压缩到 sha1sum 程序中,该程序读取数据以计算总和,然后丢弃数据,从而计算包含在 gzip 存档中的原始数据的 SHA1 总和。通过使用tee
,我们只需支付一次解压缩文件的 CPU 成本。
然后我们执行一个额外的计算成本高的步骤(用于超级额外验证),并剥离文件上的 xz 压缩(暂时,到流中)并将其通过管道传输到 sha1sum,以获得我们的“新文件”SHA1 总和。
然后我们比较两个和,如果它们不是相等的字符串,或者如果它们中的一个或两个长度为零,我们将得到一个脚本错误(退出,感谢set -e
),或者文件不会移除。else
如果需要,您可以为用户友好的错误处理实现一个子句,但是这个基本的脚本将非常安全,尽管对于交互式运行命令的用户来说不是很重要。
在年底,file.gz
将只取消链接当且仅当的未压缩的内容file.gz
,并file.xz
是在该时间点的哈希值进行计算,用天文数字般的高确定性的完全相同(坏事的可能性脚麻是什么像 1 合 1,后面有 300 个零)。此时,您只需担心此脚本退出后数据会损坏。;)
这个脚本会在几乎相同的速度在这个问题你的原始脚本运行,除外的一部分运行unxz
。幸运的是,从 LZMA 解压缩非常快,几乎和常规 Zip 一样快,并且比压缩到LZMA快一个数量级。如果您有一个快速的 CPU,并且文件足够小,这不应该给脚本增加太多的运行时间,但是如果您更看重数据完整性而不是性能,那么这显然是一个胜利。
StackOverflow 上的这个答案极大地帮助了我编写此脚本。