我想使用 zstd 动态压缩 partclone 备份。不应写入一个大型 zstd 框架,而应将存档拆分为允许半随机访问的小块。后者要求将未压缩的大小写入框架标头。当从管道向 zstd 提供数据时,需要通过--stream-size
命令行提供此信息。如果没有此要求,以下脚本将起作用:
partclone.ntfs -c -s /dev/sda1 --output - \
| split --bytes=2M --filter="zstd -1 --content-size --stdout" \
> archive.zstd
理想情况下,split
可以导出一个额外的变量CHUNKSIZE
,允许
partclone.ntfs -c -s /dev/sda1 --output - \
| split --bytes=2M --filter="zstd -1 --stream-size=$CHUNKSIZE --content-size --stdout" \
> archive.zstd
如果最后一个块较小,则仅提供--stream-size=2M
将导致错误。鉴于数据来自partclone
,循环中分块读取数据(这对于dd
从设备读取的原始数据有效)也是不可能的。出于性能考虑,存储中间文件不是一种选择。
你知道有什么split
可以导出块大小的替代品吗?或者有什么其他巧妙的技巧可以获取所需的 zstd 调用,而无需打补丁split
?谢谢!
答案1
一般情况
引擎盖下split --filter=something …
就像有很多split … | something
管道,每次一个。
这一般的管道的概念不包含有关流大小的任何预先信息,因为:
- 管道中的工具通常不知道其输出的大小,直到它读取整个输入并产生输出。
- 即使它试图以某种方式将这些信息传递给下一个工具,通常也只能在最后这样做,即在下一个工具读取(几乎)所有内容之后。此时下一个工具可以通过简单地测量其自身输入的大小来获得相同的信息,因此将这些信息从一个工具传递到另一个工具是没有意义的。
管道的设计使得工具可以并行工作,将数据流传输到另一个。设计用于管道的工具在流结束之前就开始处理其输入流。理论上,流可能永远不会结束(例如,应该cat
可以很好地与永恒流配合使用)。工具不应等待流结束,除非有充分的理由(例如,sponge
有一个理由)。
具体案例
在你的情况下,你想zstd
提前告诉每个人大小。但对于最后一个块,split
当它开始最后一个时甚至不知道这一点zstd
。事实上,split
它无法知道哪个块是最后一个。它只有在流结束时才会知道这一点partclone.ntfs
,一般来说,这是后 zstd
因为最后一个块已经开始了。
为了严格解决您的问题,某种缓冲是不可避免的,除非您能提前知道输出的确切大小partclone.ntfs
。如果您能知道这一点,那么就可以制定一个(相当繁琐和丑陋的)特定解决方案。
你不会喜欢的解决方案
由于性能原因,存储中间文件不是一种选择。
要判断哪个块是最后一个以及它的大小是多少,split
必须确切地这个。可能在 RAM 中,但仍然如此。请注意,您(或split
)不能简单地传递--stream-size=2M
除最后一个块之外的所有块,并且只对最后一个块使用中间文件。每个块都必须缓冲,因为split
事先不知道哪个块是最后一个。这意味着任何性能损失都必须影响所有块的处理,而不仅仅是最后一个块。
例子:
partclone.ntfs -c -s filesystem -o - \
| split --bytes=2M --filter='
f="$XDG_RUNTIME_DIR"/chunk
cat > "$f"
zstd -1 --rm --stdout "$f"
' > archive.zst
在我的例子中,$XDG_RUNTIME_DIR
扩展到 ,/run/user/1000
这是一个足够大的 tmpfs,可以一次额外容纳 2 MiB。根据您的需要进行调整,特别是如果您想要使用大型--bytes=
。
注意cat
s 和zstd
s 轮流运行,从不并行运行。如果您找到cat
在当前任务zstd
完成之前运行下一个任务的方法,则可能会提高性能。(显然,在这种情况下,下一个任务cat
必须写入另一个文件。)或许谨慎使用 GNUparallel --keep-order
反而可以split
幫忙。
您可能喜欢的解决方案
我的测试表明,您可以将空字节附加到由 创建的映像partclone.ntfs -c
。然后partclone.ntfs -r
不会出现任何抱怨,并且它将能够恢复文件系统。结果将是相同的,附加的空字节不会影响它。
这意味着如果我们设法将空字节附加到来自您的流的末尾partclone.ntfs -c
,那么每个块的大小始终正好是 2 MiB,那么--stream-size=2M
就可以起作用。
我们可以使用dd conv=sync
它,但我们iflag=fullblock
也需要。iflag=fullblock
不可移植,希望您dd
支持它。这是代码:
partclone.ntfs -c -s filesystem -o - \
| dd bs=2M conv=sync iflag=fullblock \
| split --bytes=2M --filter='
zstd -1 --stream-size=2M --content-size --stdout
' > archive.zst
如果不iflag=fullblock
dd
这样做,可能会在流结束之前向流中注入空字节。我们不希望出现这种情况(比较这个答案)。
该解决方案在压缩的图像末尾添加了少于 2 MiB 的内容zstd
,但由于这些是连续的空字节,因此该工具应该可以很好地压缩它们。
我可能喜欢的解决方案
我使用 Btrfs。Btrfs 支持透明文件压缩,ZSTD 是少数可用算法之一。如果我是你,我会将 的输出存储partclone.ntfs -c
在 Btrfs 中,不做任何修改;我会让文件系统处理压缩。那么所有这些麻烦split
都不zstd
需要了。
仅当我计划在文件系统之间进行复制时,显式压缩archive.zst
可能更好。如果我计划停留在单个文件系统中,并且是 Btrfs,我会选择它提供的透明压缩。