如何在没有中间文件的情况下连接多个命令的结果并通过管道传输到另一个命令?

如何在没有中间文件的情况下连接多个命令的结果并通过管道传输到另一个命令?

假设我有四个非常大的文本文件,全部用 xz 压缩。

file1.log.xz
file2.log.xz
file3.log.xz
file4.log.xz

我想做的是连接未压缩的将这四个文件的内容合并到一个新文件中file.xz。问题是,我理想地希望不必检查中间文件。

这些文件是非常大的日志文件,大小为千兆字节。压缩后,它们不到 100MB,但如果我要展开所有四个文件然后重新连接,我需要至少 30GB 的存储空间来存储未压缩的文件。当然,我可以将cat所有未压缩的文件xz重新压缩:

cat file1.log file2.log file3.log file4.log | xz -ve9 - > newfile.log.xz

我知道如何连接假设一个文件未压缩,一个文件已压缩,则在命令行中查看没有中间文件的文件:

xz -d -c file2.log.xz | cat file1.log - | xz -ve9 - > files1and2.log.xz

但这仅适用于一个文件,并且其中一个文件必须已解压缩。

我不确定是否可以将cat各种 .xz 文件放在一起 - 让我们假设它们可能已使用不同的参数进行压缩。

在更高的层面上,可以问这个问题本身:您能否获取多个(两个以上)命令的输出,连接这些输出,并将它们通过管道传输到另一个进程而不需要中间文件? (假设场景:想象我正在做某种加工在所有四个非常大的文件上使用输出到标准输出的脚本,并希望将输出放入另一个压缩文件中。)

是否可以仅使用 shell 命令来完成此操作?

答案1

文档xz

可以.xz按原样连接文件。 xz将解压缩这些文件,就好像它们是单个.xz文件一样。

根据我的测试,即使不同的文件使用不同的选项压缩,这也有效;所以在你的情况下

cat -- *.log.xz > newfile.log.xz

会工作得很好。

要回答更一般的问题,您可以通过管道传输复合命令的输出,例如

for file in -- *.log.xz; do xzcat -- "$file"; done | xz -ve9 > newfile.log.xz

或任何子外壳。这将允许您在重新压缩日志文件之前对其执行任何您想要的处理。然而在基本情况下这也是没有必要的;您可以通过运行解压缩并重新压缩所有文件

xzcat -- *.log.xz | xz -ve9 > newfile.log.xz

如果您添加-f它甚至适用于未压缩的文件,那么

xzcat -f -- uncompressed.log *.log.xz | xz -ve9 > newfile.log.xz

将允许您合并未压缩和压缩的日志。

答案2

尝试

for x in *.log.xz
do
  xz -d -c "$x"
done | xz -ve9 - > newfile.log.xz

(当然这可以上网)。

要添加新的未压缩文件,请使用子 shell ( ())

( cat newfile.log 
for x in *.log.xz
do
  xz -d -c "$x"
done ) | xz -ve9 - > newfile.log.xz

答案3

xzcat -f是你问题第一部分的答案。但你是对的:cat *xz | xzcat如果你的某些文件是用-F lzma.

在更高的层面上,可以问这个问题本身:您能否获取多个(两个以上)命令的输出,连接这些输出,并将它们通过管道传输到另一个进程而不需要中间文件?

这里的问题是:如果你不将中间输出存储在文件中你储存它吗?

如果将其存储在 RAM 中,则会受到可用 RAM 量的限制。如果你超过这个值,你的机器很快就会走上交换地狱的道路。

GNU Parallel 存储在临时文件中,但如果将它们放入tmpfs文件系统中,它们基本上存储在 RAM 中:

mkdir mytmp    
sudo mount tmpfs mytmp -t tmpfs -o rw,size=3P
parallel --tmpdir mytmp seq {}00000000 {}99999999 ::: 1 2 | grep 0000000

但是,如果可以逐行混合输出,那么您只需将每个正在运行的程序的一行存储在 RAM 中。

这就是 GNU Parallel(> 版本 20170822)所做的:

parallel --lb seq {}00000000 {}99999999 ::: 1 2 | grep 0000000

第三种解决方案是使用快速压缩器来压缩临时文件(例如pzstdpigz, lz4, lzop):

parallel --compress seq {}00000000 {}99999999 ::: 1 2 | grep 0000000

(GNU Parallel 自动检测您安装的快速压缩器)。

答案4

尽管@Archemar 谈到了这个问题,但似乎还没有人真正直接回答标题中的问题:

如何在没有中间文件的情况下连接多个命令的结果并通过管道传输到另一个命令?

并在您的帖子中重申:

在更高的层面上,可以问这个问题本身:您能否获取多个(两个以上)命令的输出,连接这些输出,并将它们通过管道传输到另一个进程而不需要中间文件?

正如Archemar 所暗示的那样,执行您所要求的操作的一般方法是使用子shell。

bash 语法:

(
  command_one
  command_two
  command_three
...
  command_N
) | next_command

相关内容