當參數從 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

相關內容