Ich habe eine Reihe von txt
Dateien, deren Namen Leerzeichen oder Sonderzeichen wie enthalten können #
.
Ich habe eine grep
Lösung grep -L "cannot have" $(grep -l "must have" *.txt)
, um alle Dateien aufzulisten, die vorhanden sind, must have
aber 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, -Z
die Liste der durch NUL getrennten Dateinamen zu drucken und xargs -r0
diese 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 zsh
bei 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 zsh
nur 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 find
stattdessen ggf. grep
einen Shell-Befehl:
find . -name '*.txt' -print0 | xargs -0 -I{} sh -c 'grep -q "must have" -- "{}" && grep -L "cannot have" -- "{}"'