Wie ignoriere ich bestimmte Dateinamen mit "find"?

143

Einer meiner Lieblings-BASH-Befehle ist:

find . -name '*.*' -exec grep 'SearchString' {} /dev/null \;

Durchsucht den Inhalt aller Dateien im und unterhalb des aktuellen Verzeichnisses nach dem angegebenen SearchString. Als Entwickler hat sich dies manchmal als nützlich erwiesen.

Aufgrund meines aktuellen Projekts und der Struktur meiner Codebasis möchte ich diesen BASH-Befehl jedoch noch weiter ausbauen, indem ich keine Dateien in oder unter einem Verzeichnis suche, das ".svn" enthält, oder Dateien, die ".svn" enthalten ende mit ".html"

Die MAN-Seite verwirrte mich allerdings. Ich habe versucht, -prune zu verwenden, und es hat mich seltsam verhalten. Bei dem Versuch, nur die HTML-Seiten zu überspringen (um zu beginnen), habe ich Folgendes versucht:

find . -wholename './*.html' -prune -exec grep 'SearchString' {} /dev/null \;

und habe nicht das Verhalten bekommen, auf das ich gehofft hatte. Ich glaube, ich verpasse möglicherweise den Punkt -Prune. Könnt ihr mir helfen?

Vielen Dank

Cody S
quelle
1
Nur zu Ihrer Information: findist kein integrierter Bash-Befehl, sondern ein separates Programm
WakiMiko
1
Sie können innerhalb der Datei mitgrep -rl 'SearchString'
emanuele
Hallo, willkommen bei SuperUser (und dem Stack Exchange-Netzwerk). Dies ist eine Frage, die ich gestellt habe und die vor zweieinhalb Jahren beantwortet wurde. Wenn Sie eine Antwort auf die Frage hinzufügen möchten, scrollen Sie in der Regel nach unten und antworten Sie dort, anstatt in einem Kommentar. Da diese Frage bereits eine akzeptierte Antwort hat (die mit dem grünen Häkchen), ist es unwahrscheinlich, dass Ihre Antwort jedoch viel Beachtung finden wird. Zu Ihrer Information.
Cody S
1
Hallo, es ist keine Antwort auf Ihre Frage. Wie Sie in der Präambel angegeben haben, handelt es sich nur um einen Tipp, der findzum Suchen in einer Datei verwendet wird.
emanuele
2
FWIW -name '*.*'findet nicht alle Dateien: nur die mit einem .in ihrem Namen (die Verwendung von *.*ist normalerweise ein DOS-Ismus, während Sie in Unix normalerweise nur *dafür verwenden). Um wirklich passen sie alle, entfernen Sie einfach das Argument zusammen: find . -exec .... Oder wenn Sie grep nur auf Dateien anwenden möchten (und Verzeichnisse überspringen möchten), dann tun Sie dies find . -type f -exec ....
Stefan

Antworten:

197

Sie können die Negativfunktion (!) Von find verwenden, um Dateien mit bestimmten Namen nicht zuzuordnen:

find . ! -name '*.html' ! -path '*.svn*' -exec grep 'SearchString' {} /dev/null \;

Wenn der Name also irgendwo im Pfad auf .html endet oder .svn enthält, stimmt er nicht überein, und die Ausführung wird nicht ausgeführt.

Paul
quelle
1
Soll ich noch -name angeben ' . 'irgendwo da drin? Würde ich das vor oder nach den Negationen tun?
Cody S
War die Absicht Ihrer *.*Übereinstimmung, sicherzustellen, dass nur Dateien mit einem übereinstimmen .? Suchen stimmt mit allen Dateien nameüberein, wenn keine Direktive vorhanden ist. Daher stimmt das oben Gesagte mit allen Dateien außer html und svn überein
Paul
5
Ich denke du willst -wholename '*.svn*'lieber als -name.
fuenfundachtzig
2
Ja, damit die .svnVerzeichnisse aus den Suchergebnissen ausgeschlossen werden.
fuenfundachtzig
1
@Noumenon ! -name '.'sollte .aus den Suchergebnissen ausschließen .
Paul
11

Ich habe seit langer Zeit das gleiche Problem und es gibt verschiedene Lösungen, die in verschiedenen Situationen anwendbar sind:

  • ack-grepist eine Art "Entwickler grep", der standardmäßig Versionskontrollverzeichnisse und temporäre Dateien überspringt. Auf der manSeite wird erläutert, wie Sie nur bestimmte Dateitypen durchsuchen und eigene definieren .
  • grepDie Optionen own --excludeund --exclude-dirkönnen sehr einfach verwendet werden, um Datei- Globs und einzelne Verzeichnisse zu überspringen (leider kein Globbing für Verzeichnisse).
  • find . \( -type d -name '.svn' -o -type f -name '*.html' \) -prune -o -print0 | xargs -0 grep ... sollte funktionieren, aber die oben genannten Optionen sind auf lange Sicht wahrscheinlich weniger problematisch.
l0b0
quelle
9

Der folgende findBefehl löscht Verzeichnisse, deren Namen enthalten .svn . Obwohl er nicht in das Verzeichnis abfällt, wird der Name des gelöschten Pfads gedruckt ... ( -name '*.svn'ist die Ursache!).

Sie können die Verzeichnisnamen herausfiltern über grep -d skip:, wodurch solche eingegebenen "Verzeichnisnamen" unbemerkt übersprungen werden.

Mit GNU grep können Sie -Hanstelle von verwenden /dev/null. Als kleines Nebenthema: \+Kann viel schneller sein als \;z. Für 1 Million einzeilige Dateien \;dauerte die Verwendung 4 Minuten und 20 Sekunden . Die Verwendung \+dauerte nur 1,2 Sekunden .

Die folgende Methode verwendet xargsstatt -execund nimmt es keine Zeilenumbrüche sind \nin Ihren Dateinamen . Wie hier verwendet, xargsist so ziemlich dasselbe wie bei find's \+.

xargsSie können Dateinamen, die aufeinanderfolgende Leerzeichen enthalten, übergeben, indem Sie das Eingabetrennzeichen '\n'mit der -dOption auf ändern .

Dies schließt Verzeichnisse , deren Namen enthalten .svn und greps nur Dateien , die mit nicht enden .html.

find . \( -name '*.svn*' -prune  -o ! -name '*.html' \) |
   xargs -d '\n' grep -Hd skip 'SearchString'
Peter.O
quelle
1
Vielen Dank für den Hinweis auf die \+Variante der Aktion -exec. Hurra für leichte Nebenprobleme!
Christian Long
Da +es sich bei der Shell nicht um ein Sonderzeichen handelt, müssen Sie natürlich nicht \vorher tippen .
Scott