Zgrep stoppt nach der ersten Übereinstimmung, wenn Argumente von xargs übergeben werden

Zgrep stoppt nach der ersten Übereinstimmung, wenn Argumente von xargs übergeben werden

Ich verwende diesen Befehl, um Muster in Zip-Dateien zu finden (ähnlich dem hier vorgeschlagenen) https://superuser.com/questions/144926/unix-grep-für-eine-zeichenfolge-innerhalb-aller-gzip-dateien-in-allen-unterverzeichnissen

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

Das Grepping wird nach dem ersten Treffer fortgesetzt. Wahrscheinlich ist find/ xargsder Übeltäter. Wie kann ich die Suche nach grepdem ersten Treffer beenden?

PSWie stoppe ich den Suchbefehl nach dem ersten Treffer?funktioniert nicht, da findnach einer Übereinstimmung gestoppt werden muss, die auf grep erfolgreich ist, und nicht nur nach der ersten Übereinstimmung von find.

Antwort1

Verschiedene Dinge:

  • zgrepdient zum Durchsuchen .zkomprimierter .gzDateien, nicht von Dateien in komprimierten zipArchiven.

    Manchmal ist ein (defektes) zipgrepSkript im Lieferumfang enthalten unzip, um in Archiven zu suchen . Es wird jedoch auf jedem Mitglied des Archivs zipausgeführt (sodass „with each“ die erste Übereinstimmung für jede Datei melden würde).egrep-m1egrep

    zgrep, ähnlich ist ein mitgeliefertes Skript, gzipdas die Ausgabe von für jede Datei einspeist gzip -cdfq. grepkann Dateien gzip -ddekomprimieren zip, tut dies aber nur für das erste Mitglied des Archivs und nur, wenn es komprimiert ist (in zipDateien sind nicht unbedingt alle Mitglieder komprimiert, insbesondere kleine).

  • xargsführt so wenige Befehle wie nötig aus, kann aber trotzdem mehrere ausführen, wenn die Dateiliste groß ist.

zipgrepHier ist die Implementierung per Hand (hier mit GNU-Tools) wahrscheinlich die beste Lösung :

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

Dadurch wird eine Shell pro Datei ausgeführt, aber das wäre zipgrepauch zipgrepbei vielen weiteren Befehlen der Fall.

Es kann fehlschlagen, wenn die Namen der Archivmitglieder Platzhalterzeichen ( *, [, ?) oder andere Zeichen wie die ASCII-Zeichen 0x1 bis 0x1f und verschiedene andere enthalten. Dies liegt jedoch hauptsächlich an Fehlern und Einschränkungen in unzipund ist nicht so schlimm wie bei der Verwendung von zipgrep.

Antwort2

Versuchen:

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

Ich habe -inameanstelle von verwendet -regex– es funktioniert hierfür genauso gut und ist meiner Meinung nach weniger verwirrend als finddie seltsame Regex-Verarbeitung von . -print0und xargs -0werden verwendet, damit alle Dateinamen mit Leerzeichen oder Shell-Metazeichen korrekt verarbeitet werden.

grepDie -lOption ist in der Manpage dokumentiert:

   -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.

Die erste angegebene Übereinstimmung gilt pro Datei. Wenn also mehrere Dateien übereinstimmen, werden sie alle gedruckt. Beachten Sie, dass dies bedeutet, dass grep mit der Suche in den anderen Dateien fortfährt, auch nachdem es eine Übereinstimmung gefunden hat.

Wenn Sie möchten, dass es nach der allerersten Übereinstimmung stoppt, können Sie die Option grepvon verwenden --line-bufferedund die Ausgabe von grep in umleiten head -1. Wenn die erste Übereinstimmung gedruckt wird, headwird sie gedruckt und beendet, grephat keine Standardausgabe mehr, wird also beendet und findfolgt.

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

Antwort3

grepDie Option 's (oder zgrep's) -mführt dazu, dass das Lesen deraktuelle Dateizum ersten Spiel:

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

Das wird sie nicht davon abhalten, dienächsteDatei. Beispiel:

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

Das Problem ist also nicht xargs, dass Sie mehrere Dateien greppen. Um nach der ersten Übereinstimmung anzuhalten grep(oder anzuhalten),zgrepDatei, müssten Sie eine kleine Schleife ausführen, wie @Stephane vorgeschlagen hat. Oder so etwas wie das hier mit bash :

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

Oder für Zip-Archive, diemehrere Dateien enthalten(danke @Stephane):

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

Antwort4

grep -m 1listet die erste Übereinstimmung jeder Datei auf.

Es gibt eine einfache Möglichkeit, nur die erste Übereinstimmung aufzulisten: pipe through head -n 1. Die Suche wird bald an einemSIGPIPE.

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

verwandte Informationen