当参数从 xargs 传递时,Zgrep 在第一次匹配后停止

当参数从 xargs 传递时,Zgrep 在第一次匹配后停止

我使用此命令来查找 zip 文件中的模式(类似于此处建议的模式) https://superuser.com/questions/144926/unix-grep-for-a-string-within-all-gzip-files-in-all-subdirectories

find . -regex ".*/.*zip" | xargs zgrep -m 1 -E "PATTERN"

第一场比赛结束后,Greping 仍在继续。可能find/xargs是罪魁祸首。如何在grep找到第一个匹配项后停止查找?

聚苯乙烯如何在第一次匹配后停止查找命令?不起作用,因为find需要在 grep 成功的匹配之后停止,而不仅仅是 find 的第一个匹配。

答案1

几件事:

  • zgrep是查看压缩文件,.z而不是压缩档案.gz内的文件。zip

    有一个(损坏的)zipgrep脚本有时与unzip, 捆绑在一起,用于查看zip档案,但它的作用是egrep在档案的每个成员上运行(因此-m1每个成员egrep都会报告每个文件的第一个匹配项)。

    zgrep,类似的是一个附带的脚本,它为每个文件提供togzip的输出。可以解压缩文件,但仅对存档的第一个成员执行此操作,并且仅当它被压缩时(在文件中,并非所有成员都必须被压缩,尤其是小成员)。gzip -cdfqgrepgzip -dzipzip

  • xargs根据需要运行尽可能少的命令,但如果文件列表很大,它仍然可能运行多个命令。

在这里,你最好的选择可能是zipgrep手动实现(这里使用 GNU 工具):

find . -name '*.zip' -type f -exec sh -c '
    unzip -Z1 "$1" |
      while IFS= read -r file; do
        unzip -p "$1" "$file" | grep --label="$1//$file" -Hm1 -- "$0" && exit
      done' PATTERN {} \; -quit

每个文件运行一个 shell,但也会zipgrep运行zipgrep更多命令。

如果存档成员的名称包含通配符 ( *, [, ?) 或其他字符(如 ASCII 字符 0x1 到 0x1f 以及各种其他字符),则可能会失败,但这主要是由于 中的错误和限制unzip,而且这并不像使用 时那么糟糕zipgrep

答案2

尝试:

find . -iname '*.zip' -print0 | xargs -0r zgrep -l -E 'PATTERN'

我用过-iname而不是-regex- 它对此也很有效,并且在我看来,比find奇怪的正则表达式处理更容易混淆。 使用-print0xargs -0以便正确处理其中包含空格或 shell 元字符的任何文件名。

grep-l选项记录在手册页中:

   -l, --files-with-matches
          Suppress  normal  output;  instead  print the name of each input
          file from which output would normally have  been  printed.   The
          scanning  will  stop  on  the  first match.

提到的第一个匹配是每个文件,因此如果多个文件匹配,它们都会被打印。请注意,这意味着 grep 将继续搜索其他文件,即使在找到一个匹配项之后也是如此。

如果您希望它在第一个匹配后停止,您可以使用greps--line-buffered选项并将 grep 的输出通过管道传输到head -1.当打印第一个匹配时,head将打印它并终止,grep将不再有标准输出,因此它将终止,find并将跟随。

find . -iname '*.zip' -print0 | xargs -0r zgrep --line-buffered -l -E 'PATTERN' | head -1

答案3

grep的(或zgrep-m选项将导致它停止读取当前文件在第一场比赛中:

   -m NUM, --max-count=NUM
          Stop reading a file after NUM matching lines.  

这不会阻止它搜索下一个文件。例如:

$ echo "hello" > foo
$ echo "hello" > bar
$ grep -m 1 hello foo bar
foo:hello
bar:hello

所以,问题不xargs在于您正在 grep 多个文件。为了在第一次匹配后停止grep(或)zgrep文件,你必须像@Stephane建议的那样运行一个小循环。或者,用 bash 进行类似的操作:

shopt -s globstar
for i in **/*.zip; do
  zgrep -l pattern "$i" && break; 
done

或者,对于 zip 档案包含多个文件(感谢@Stephane):

shopt -s globstar
for i in **/*.zip; do
  if unzip -p "$i" | grep -q hello; then 
    echo "$i" && break;
  fi;
done

答案4

grep -m 1列出每个文件的第一个匹配项。

有一个简单的方法可以列出第一个匹配项:通过管道head -n 1。搜索很快就会因信号管道

find . -regex ".*/.*zip" -print0 | xargs -0 zgrep -E "PATTERN" | head -n 1

相关内容