如何在 tar 时重定向 xz 的正常标准输出 | xz?

How to redirect xz's normal stdout when do tar | xz?

我需要使用像 xz 这样的压缩器来压缩巨大的 tar 档案。

我完全了解之前的问题,例如 Create a tar.xz in one commandUtilizing multi core for tar+gzip/bzip compression/decompression

从他们那里,我发现这个命令行最有效:

tar -cvf - paths_to_archive | xz -1 -T0 -v > OUTPUT_FILE.tar.xz

我使用管道解决方案,因为我绝对必须能够将选项传递给 xz。 特别是,xz 非常 CPU 密集,所以我必须使用 - T0 使用所有可用的内核。这就是为什么我没有使用其他可能性,例如 tar 的 --use-compress-program 或 -J 选项。

不幸的是,我真的很想将所有 tar 和 xz 的日志输出(即非存档输出)捕获到一个日志文件中。在上面的示例中,注销总是由那些 -v 选项生成。

使用上面的命令行,日志输出现在打印在我的终端上。

所以,问题是当你像上面那样使用管道连接tar和xz时,你不能用类似

的东西结束命令行
>Log_File  2>&1

因为之前那个

> OUTPUT_FILE.tar.xz

有解决办法吗?

我试过像这样包装在子外壳中

(tar -cvf - paths_to_archive | xz -1 -T0 -v > OUTPUT_FILE.tar.xz) >Log_File  2>&1

但这没有用。

第一个-cvf -可以换成cv.

但是 tar cvf - 的正常标准输出是 tar 文件,它通过管道传输到 xz。不确定我是否完全理解,也许是这样:

tar cv paths | xz -1 -T0 > OUTPUT.tar.xz 2> LOG.stderr

tar cv paths 2> LOG.stderr | xz -1 -T0 > OUTPUT.tar.xz

tar cv paths 2> LOG.tar.stderr | xz -1 -T0 > OUTPUT.tar.xz 2> LOG.xz.stderr

不确定 -T0 是否已实现,您使用的是哪个版本的 xz? (也许 https://github.com/vasi/pixz 值得仔细研究)pv 程序在某些系统上与 sudo apt-get install pv 一起安装,比 xz -v 更能显示管道的进度。它会以百分比的形式告诉您预计到达时间的进度:

size=$(du -bc path1 path2 | tail -1 | awk '{print}')
tar c paths 2> LOG.stderr | pv -s$size | xz -1 -T0 > OUTPUT.tar.xz

tar 的标准输出是压缩包,xz 的标准输出是压缩文件。 None 这些东西是您应该要捕获的日志。 除输出文件本身之外的所有 日志记录都专门写入两个进程的 stderr。

因此,您只需要重定向 stderr,并且 绝不能 重定向 stdout,除非您希望输出文件与日志记录混淆。

{ tar -cvf - paths_to_archive | xz -1 -T0 -v > OUTPUT_FILE.tar.xz; } 2>Log_File

顺便说一句——如果您对 xz -v 输出到 TTY 时打印更多内容感到好奇,答案是 in this line of message.cprogress_automatic 标志(告诉 xz 设置一个定时器来触发 SIGALRM——它认为这是应该打印状态的指示——每秒)仅在 isatty(STDERR_FILENO) 为真时设置。因此,在 stderr 被重定向到一个文件后,xz 根本不再打印这个输出;问题不是它没有正确重定向,而是它 不再存在 .

但是,如果您真的愿意,您可以通过自己的代码每秒将 SIGALRM 发送到 xz

{
  xz -1 -T0 -v > OUTPUT_FILE.tar.xz < <(tar -cvf - paths_to_archive) & xz_pid=$!
  while sleep 1; do
    kill -ALRM "$xz_pid" || break
  done
  wait "$xz_pid"
} 2>Log_File

(避免将 xz 执行所需时间四舍五入到最接近的秒数的代码是可能的,但作为练习留给 reader)。