首页 > 解决方案 > 为什么 zgrep 不显示实际匹配项?

问题描述

语境

假设我有两个文件a.txtb.txt一些内容......

$ tail *.txt
==> a.txt <==
ABC
CDE
123
C

==> b.txt <==
C
321
EDC
CBA

让我们也想象一下,这些文件现在已经被放入一个 gzip 压缩包中......

$ tar -czf tarball.tgz *.txt
$ tar -tf tarball.tgz
a.txt
b.txt

目标

现在,我想通过 tarball 中的文件 grep。在比赛前看到原始文件名和行号会很好,但我最重要的是想看到匹配的行。

我尝试了什么?

首先,我预计这zgrep 'pattern' tarball.tgz会简单地工作。它确实告诉我是否有匹配,它甚至可以计算它们,但我找不到打印匹配的方法......

$ zgrep 'AB' tarball.tgz
Binary file (standard input) matches
$ zgrep 'C' tarball.tgz
Binary file (standard input) matches
$ zgrep -c 'AB' tarball.tgz
1
$ zgrep -c 'C' tarball.tgz
6

其次,我想到zcat了 tarball 并在其上使用常规 grep。但是,我仍然收到完全相同的“二进制文件(标准输入)匹配”消息...

$ zcat tarball.tgz | grep 'C'
Binary file (standard input) matches

我猜zcat(和zgrep)做gunzip但没有tar -xf?如果我看一下,zcat我可以看到与刚刚完成的输出相同的输出tar -c......

$ zcat tarball.tgz
a.txt0000664�3���3���0000000001613554050266013370 0ustar  useruserABC
CDE
123
C
b.txt0000664�3���3���0000000001613554050301013357 0ustar  useruserC
321
EDC
CBA

$ tar -c *.txt
a.txt0000664�3���3���0000000001613554050266013370 0ustar  useruserABC
CDE
123
C
b.txt0000664�3���3���0000000001613554050301013357 0ustar  useruserC
321
EDC
CBA

所以最后,我得到了这个工作正常的解决方案:

$ tar -xOzf tarball.tgz | grep 'C'
ABC
CDE
C
C
EDC
CBA

当然,如果我现在询问文件名和行号,我没有得到任何有用的信息......

$ tar -xOzf tarball.tgz | grep -Hn 'C'
(standard input):1:ABC
(standard input):2:CDE
(standard input):4:C
(standard input):5:C
(standard input):7:EDC
(standard input):8:CBA

我能想到的唯一方法是获得我想要的结果,需要更多的脚本来提取压缩包并grep循环运行......


有没有一种很好(简单而简洁)的方法来做到这一点?

标签: shellzgrep

解决方案


tar -czf做两件事:

  • 将所有文件(在我的示例中恰好是文本)打包成一个 tar 文件(它是二进制文件);
  • 将该 tar 文件 gzip 到 gzip 压缩的 tar 文件中。

正如我所怀疑的那样,zgrep或者zcat只会做一个gunzip, 并留下一个仍然是二进制的 tar 文件。这解释了我得到的所有输出。

简单的解决方案

最简单的方法是添加一个选项zgrep

   -a, --text
          Process a binary file as if it were text; this is equivalent to the --binary-files=text option.

这几乎和 . 一样好tar -xOzf tarball.tgz | grep -Hn 'C',我们没有得到单独的文件名,并且行号在整个 tar 输出上。我们也得到了一些噪音,即tar格式:

$ zgrep -Hna 'C' tarball.tgz
tarball.tgz:1:a.txt0000664�3���3���0000000001613554050266013370 0ustar  jlehuenjlehuenABC
tarball.tgz:2:CDE
tarball.tgz:4:C
tarball.tgz:5:b.txt0000664�3���3���0000000001613554050301013357 0ustar  jlehuenjlehuenC
tarball.tgz:7:EDC
tarball.tgz:8:CBA

这很容易记住,并且对于例如文件的第一行很少是有趣匹配的 grepping 日志非常有效。

最佳输出

现在,@Shawn 向我指出了 Unix StackExchange上的那个答案。由此,我可以提出我最喜欢的选择:

$ tar -xf tarball.tgz --to-command='grep -Hn --label="$TAR_ARCHIVE/$TAR_FILENAME" C || true'
tarball.tgz/a.txt:1:ABC
tarball.tgz/a.txt:2:CDE
tarball.tgz/a.txt:4:C
tarball.tgz/b.txt:1:C
tarball.tgz/b.txt:3:EDC
tarball.tgz/b.txt:4:CBA

我可能会为此创建一些函数,因为打字不好玩。不过,输出正是我想要的!:)


推荐阅读