Führen Sie "grep" ohne eine Datei in einem bestimmten Pfad aus


12

Ich möchte die Datei ./test/main.cppvon meiner Suche ausschließen.

Folgendes sehe ich:

$ grep -r pattern --exclude=./test/main.cpp
./test/main.cpp:pattern
./lib/main.cpp:pattern
./src/main.cpp:pattern

Ich weiß, dass es möglich ist, die gewünschte Ausgabe mit mehreren Befehlen in einer Pipes-and-Filters-Anordnung zu erhalten, aber gibt es einige Anführungszeichen / Escapezeichen, die grepverstehen lassen , was ich von Haus aus will?


Eine Lösung, die auf dem Filtern der Ausgabe basiert, lässt sich nicht gut skalieren, da die Datei unnötigerweise durchsucht wird, bevor die zugehörigen Ergebnisse ausgeschlossen werden. Das Problem wird vergrößert, wenn ich ganze Verzeichnisse (mit --exclude-dir) ausschließen möchte . Deshalb möchte ich grep den Ausschluss nativ ausführen lassen.
Am

1
--exclude gibt an, dass glob kein Pfad ist
PersianGulf

Antworten:


6

grep Dies ist für Dateien in einem bestimmten Verzeichnis nicht möglich, wenn Sie mehrere Dateien mit demselben Namen in verschiedenen Verzeichnissen haben. Verwenden Sie stattdessen find:

find . -type f \! -path './test/main.cpp' -exec grep pattern {} \+


Warum fliehst du \!und \+? Es scheint gut zu funktionieren, ohne die Backslashes.
Am

@nobar Ich bin daran gewöhnt, weil einige Zeichen Shell-Schlüsselwörter sind. Sie werden also nie überrascht sein, weil nichts passieren kann, wenn sie ausgeblendet werden.
MichalH

" grepkann das nicht, findstattdessen verwenden" - perfekt.
Am

4

Ich denke nicht, dass es mit GNU möglich ist grep. Du brauchst aber keine Rohre.

Mit find:

find . ! -path ./test/main.cpp -type f -exec grep pattern {} +

Mit zsh:

grep pattern ./**/*~./test/main.cpp(.)

(Ausgeschlossen sind versteckte Dateien, ebenso das .git, .svn ...).


2

Ich könnte ein Buch schreiben: "Die verlorene Kunst von xargs". Das find ... -exec … ';startet ein Grep für jede Datei (aber die Variante mit -exec … +nicht). Nun, wir verschwenden heutzutage CPU-Zyklen. Warum also nicht? Aber wenn Leistung, Gedächtnis und Energie ein Problem sind: benutze xargs:

find . -type f \! -path 'EXCLUDE-FILE' -print0 | xargs -r0 grep 'PATTERN'

GNU find‚s -print0wird NULseine Ausgabe und -terminate xargs-0Option ehrt das Format als Eingabe. Dadurch wird sichergestellt, dass die Pipeline nicht durcheinander gerät, unabhängig davon, welche lustigen Zeichen Ihre Datei enthält. Die -rOption stellt sicher, dass kein Fehler vorliegt, falls findnichts gefunden wird.

Beachten Sie, dass Sie jetzt Folgendes ausführen können:

find . -type f -print0 | grep -z -v "FILENAME EXCLUDE PATTERN" | 
  xargs -r0 grep 'PATTERN'

GNU grep -zmacht dasselbe wie xargs -0.


3
Einige interessante Hinweise, aber ich bin mir nicht sicher, ob Sie in Bezug auf das Leistungsproblem richtig liegen. Soweit ich weiß, find -exec (cmd) {} +funktioniert es genauso wie xargsund find -exec (cmd) {} \;funktioniert genauso wie xargs -n1. Mit anderen Worten, Ihre Aussage ist nur richtig, wenn die \;Version verwendet wird.
Am

3
Das Einleiten xargsist weniger effizient als das Verwenden -exec … +(wenn auch nur geringfügig). Keine der Antworten hier erwähnt auch nur -exec … \;.
Gilles 'SO- hör auf böse zu sein'

1
Nun, s - t. Ich datiere mich. Danke für die Kommentare und Korrekturen. Ich dachte, das \ + sei ein Tippfehler. Oh, schau mal, -exec ... +hinzugefügt im Januar 2005. Ja, ich bin nicht veraltet ... überhaupt ....
Otheus

2

Wenn Ihre findUnterstützungen, -pathdie 2008 zu POSIX hinzugefügt wurden, in Solaris jedoch noch fehlen:

find . ! -path ./test/main.cpp -type f -exec grep pattern /dev/null {} +

1
Ich glaube nicht, dass das funktionieren wird, da nobar main.cpp in anderen Verzeichnissen haben möchte
Eric Renouf

1
Schließt Ihr Pattern main.cpp nicht auch von allen anderen Verzeichnissen aus? Das wäre nicht wünschenswert
Eric Renouf

@EricRenouf: Oh, mein Fehler, eine falsche Lesart. Aktualisiert meine Antwort.
Donnerstag,

@ Gilles: Warum -pathist nicht POSIX?
Dienstag,

Ah, entschuldigung, mein Fehler, es wurde 2008 hinzugefügt. In Solaris fehlt es jedoch noch.
Gilles 'SO- hör auf böse zu sein'

1

Für die Aufzeichnung ist hier der Ansatz, den ich bevorzuge:

grep pattern $(find . -type f ! -path './test/main.cpp')

grepIch halte das am Anfang des Befehls, was meiner Meinung nach ein wenig klarer ist - außerdem wird die Farbhervorhebung nicht deaktiviert grep. In gewissem Sinne ist die Verwendung findin einer Befehlsersetzung nur eine Möglichkeit, die (begrenzte) Dateisuchteilmenge der grepFunktionalität von zu erweitern / zu ersetzen .


Für mich ist die find -execSyntax irgendwie arkan. Eine Schwierigkeit find -execist die (manchmal) Notwendigkeit, verschiedene Zeichen zu maskieren (insbesondere, wenn \;sie unter Bash verwendet werden). Nur um die Dinge in einen vertrauten Kontext zu bringen, sind die folgenden zwei Befehle im Grunde gleichbedeutend:

find . ! -path ./test/main.cpp -type f -exec grep pattern {} +
find . ! -path ./test/main.cpp -type f -print0 |xargs -0 grep pattern

Wenn Sie Unterverzeichnisse ausschließen möchten , müssen Sie möglicherweise einen Platzhalter verwenden. Ich verstehe das Schema hier nicht ganz - rede über Arkan :

grep pattern $(find . -type f ! -path './test/main.cpp' ! -path './lib/*' )

Ein weiterer Hinweis zur Verallgemeinerung von findLösungen zur Verwendung in Skripten : Die grepBefehlszeile sollte die Option -H/ enthalten --with-filename. Andernfalls wird die Ausgabeformatierung geändert, wenn die Suchergebnisse von nur einen Dateinamen enthalten find. Dies ist bemerkenswert, da es bei Verwendung grepder nativen Dateisuche (mit der -rOption) nicht erforderlich zu sein scheint .

... Noch besser ist es jedoch, /dev/nullals erste zu durchsuchende Datei einzuschließen. Dies löst zwei Probleme:

  • Es stellt sicher, dass bei einer zu durchsuchenden Datei grepzwei Dateien vorhanden sind und der Ausgabemodus für mehrere Dateien verwendet wird.
  • Es stellt sicher, dass, wenn keine zu durchsuchenden Dateien vorhanden sind, angenommen grepwird, dass eine Datei vorhanden ist, und das Warten auf stdin nicht unterbrochen wird.

Die endgültige Antwort lautet also:

grep pattern /dev/null $(find . -type f ! -path './test/main.cpp')

Sie sollten die Ausgabe von nicht findin einer Befehlsersetzung verwenden. Dies bricht ab, wenn Dateinamen Leerzeichen oder andere Sonderzeichen enthalten. Verwendung find -exec, es ist robust und einfach zu bedienen.
Gilles 'SO- hör auf böse zu sein'

@Gilles: Sehr guter Punkt - auch die Ausgabe könnte möglicherweise die Grenzwerte für die Befehlszeilengröße einiger Programme überschreiten. Vorbehalt Emptor.
Am

Pfui. Die 'find'-Syntax ist fürchterlich schwierig. '-o' ist ein "oder" Operator (auch '-or' unter Linux), aber seine typische Verwendung (zum Beispiel mit '-prune') entspricht konzeptionell nicht dem Begriff eines logischen oder. Es ist eher ein funktionales oder als ein logisches oder.
Nobar

Eine weitere Möglichkeit , Unterverzeichnisse auszuschließen , basierend auf einen Namen übereinstimmt , gefunden find -iname "*target*" -or -name 'exclude' -prune. Nun, es funktioniert irgendwie - das beschnittene Verzeichnis wird aufgelistet, aber nicht durchsucht. Wenn Sie nicht möchten, dass es aufgeführt wird, können Sie eine Art redundantes! -name 'exclude'
nobar
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.