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"

最初の一致後も Grepp は続行されます。おそらくfind/xargsが原因です。grep最初の一致が見つかった後、検索を停止するにはどうすればよいでしょうか?

追伸最初の一致後に find コマンドを停止するにはどうすればよいですか?findfind の最初の一致だけでなく、grep に成功する一致の後に停止する必要があるため、機能しません。

答え1

いくつかのこと:

  • zgrep圧縮されたアーカイブ内のファイルではなく、圧縮されたファイルを調べることです.z.gzzip

    には、アーカイブを調べるための(壊れた)zipgrepスクリプトがバンドルされていることがあります。ただし、このスクリプトは、アーカイブの各メンバーに対して実行されます(つまり、 eachでは、各ファイルの最初の一致が報告されます)。unzipzipegrep-m1egrep

    zgrep同様に、 には、各ファイルに対して の出力を に送るスクリプトが付属しています。はgzipファイルを解凍できますが、アーカイブの最初のメンバーに対してのみ、かつそれが圧縮されている場合にのみ解凍します (ファイルでは、すべてのメンバーが必ずしも圧縮されているわけではなく、特に小さいメンバーはそうではありません)。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

これはファイルごとに 1 つのシェルを実行しますが、さらに多くのコマンドも実行しますzipgrepzipgrep

アーカイブ メンバーの名前にワイルドカード文字 ( *、、) や ASCII 文字 0x1 から 0x1f などのさまざまな文字が含まれている場合は失敗する可能性がありますが、これは主に のバグや制限によるもので[、を使用する場合ほど悪くはありません。?unzipzipgrep

答え2

試す:

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

私は-inameではなくを使用しました-regex。これはこの場合も同様に機能し、findの奇妙な正規表現の処理よりも混乱が少ないと、私 は考えています。-print0および はxargs -0、スペースやシェルのメタ文字を含むファイル名が正しく処理されるために使用されます。

grep-lオプションについては、man ページに記載されています。

   -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 は 1 つの一致を見つけた後でも、他のファイルの検索を続行することに注意してください。

最初の一致の後で停止したい場合は、grep--line-bufferedオプションを使用して、grep の出力を にパイプすることができますhead -1。最初の一致が印刷されると、headはそれを印刷して終了し、grepには標準出力がなくなるため終了し、 がfind続きます。

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

答え3

grep's(またはzgrep's)-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。検索はすぐに終了し、SIGPIPE の

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

関連情報