在 split--filter 命令中获取块大小?

在 split--filter 命令中获取块大小?

我想使用 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管道,每次一个。

一般的管道的概念不包含有关流大小的任何预先信息,因为:

  1. 管道中的工具通常不知道其输出的大小,直到它读取整个输入并产生输出。
  2. 即使它试图以某种方式将这些信息传递给下一个工具,通常也只能在最后这样做,即在下一个工具读取(几乎)所有内容之后。此时下一个工具可以通过简单地测量其自身输入的大小来获得相同的信息,因此将这些信息从一个工具传递到另一个工具是没有意义的。

管道的设计使得工具可以并行工作,将数据流传输到另一个。设计用于管道的工具在流结束之前就开始处理其输入流。理论上,流可能永远不会结束(例如,应该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=

注意cats 和zstds 轮流运行,从不并行运行。如果您找到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,我会选择它提供的透明压缩。

相关内容