Grep für spezielle Dateinamen nutzbar machen

Grep für spezielle Dateinamen nutzbar machen

Ich habe eine Reihe von txtDateien, deren Namen Leerzeichen oder Sonderzeichen wie enthalten können #.

Ich habe eine grepLösung grep -L "cannot have" $(grep -l "must have" *.txt), um alle Dateien aufzulisten, die vorhanden sind, must haveaber nicht cannot have.

Beispielsweise gibt es eine Datei abc defg.txt, die nur eine Zeile enthält: must have.

Normalerweise sollte die Grep-Lösung das herausfinden abc defg.txt, aber sie gibt zurück:

grep: abc: No such file or directory
grep: defg.txt: No such file or directory

Ich denke, für Dateinamen, die enthalten #, ist die Grep-Lösung auch ungültig.

Kann mir jemand helfen, die Grep-Lösung zu ändern?

Antwort1

WENNSie sind bereit, weiter weg zu gehen? Awk kann dies in einem Durchgang erledigen:

awk 'function s(){if(a&&!b){print f}} FNR==1{s();f=FILENAME;a=b=0} 
  /must have/{a=1} /cannot have/{b=1} END{s()}' filepattern

Für aktuelleres Gawk können Sie es mit BEGINFILE und ENDFILE vereinfachen. (Wie bei allen Awk-Antworten können Sie die Awk-Befehle mit -f in eine Datei einfügen und wie bei den meisten können Sie sie bei Bedarf problemlos in Perl konvertieren.)

Antwort2

Da Sie bereits GNU-spezifische Optionen () verwenden -L, können Sie Folgendes tun:

grep -lZ -- "must have" *.txt | xargs -r0 grep -L -- "cannot have"

Die Idee besteht darin, -Zdie Liste der durch NUL getrennten Dateinamen zu drucken und xargs -r0diese Liste dann als Argumente an das zweite zu übergeben grep.

Bei der Befehlsersetzung werden standardmäßig Leerzeichen, Tabulatoren und Zeilenumbrüche (und NUL in zsh) verwendet. Bourne-ähnliche Shells führen außer zshbei jedem Wort, das aus dieser Teilung resultiert, auch Globbing durch.

Du könntest es tun:

IFS='
' # split on newline only
set -f # disable globbing
grep -L -- "cannot have" $(
    set +f # we need globbing for *.txt in this subshell though
    grep -l -- "must have" *.txt
  )

Bei Dateinamen mit Zeilenumbruchzeichen würde das jedoch immer noch nicht funktionieren.

In zsh(und zshnur in) können Sie Folgendes tun:

IFS=$'\0'
grep -L -- "cannot have" $(grep -lZ -- "must have" *.txt)

Oder:

grep -L -- "cannot have" ${(ps:\0:)"$(grep -lZ -- "must have" *.txt)"}

Antwort3

Verwenden Sie findstattdessen ggf. grepeinen Shell-Befehl:

find . -name '*.txt' -print0 | xargs -0 -I{} sh -c 'grep -q "must have" -- "{}" && grep -L "cannot have" -- "{}"'

verwandte Informationen