Rekursives grep für Wörter in einem bestimmten Dateityp

7

Ich wollte einen Befehlszeilenbefehl, um alle Shell-Skripte im Dateisystem nach einem bestimmten Wort zu durchsuchen, also fragte ich bei der Arbeit herum und bekam die folgenden Lösungen:

grep word `find / -name \*.sh 2>/dev/null`
find / -name "*.sh" 2>/dev/null | xargs grep word

Ich bin jedoch mit der Befehlszeile nicht so vertraut, daher erscheinen mir beide Lösungen undurchsichtig. Ich würde es vorziehen, etwas zu tun, das aussieht wie:

ls -r *.sh | cat | grep -H word

Aber es scheint, dass Sie keine Dateinamen in cat leiten können (zumindest denke ich, dass das das Problem ist).

Was ist die am besten lesbare Lösung? Und zweitens, was ist die effizienteste Lösung?

Bearbeiten: Ich musste wissen, in welcher Datei das Wort gefunden wurde, damit ich das Skript ändern konnte.

paulrehkugler
quelle

Antworten:

8

Bearbeiten: Wenn Sie über GNU-Dienstprogramme verfügen, finden Sie in Gilles 'Antwort eine Methode, grepdie die Rekursionsfähigkeiten von GNU verwendet und viel einfacher ist als der findAnsatz. Wenn Sie nur Dateinamen anzeigen möchten, möchten Sie dennoch die -lunten beschriebene Option hinzufügen .


Verwenden Sie grep -l worddiese Option, um nur Namen von Dateien zu drucken, die eine Übereinstimmung enthalten.

Wenn Sie alle Dateien im Dateisystem finden möchten .sh, die am Stamm beginnen /, findist dies das am besten geeignete Tool.

Die tragbarste und effizienteste Empfehlung lautet:

find / -type f -name '*.sh' -exec grep -l word {} + 2>/dev/null

Dies ist ungefähr so ​​lesbar wie es nur geht und ist nicht schwer zu analysieren, wenn Sie die Semantik hinter jeder der Komponenten verstehen.

  • find /: findAusgehend vom Dateisystemstamm ausführen ,/
  • -type f: Nur mit regulären Dateien übereinstimmen
  • -name '*.sh': ... und stimmen nur mit Dateien überein, deren Namen auf enden .sh
  • -exec ... {} +: Befehl ausführen, der in ...übereinstimmenden Dateien in Gruppen angegeben ist, wobei {}er durch die Dateinamen in der Gruppe ersetzt wird. Die Idee ist, den Befehl innerhalb der Grenzen des Systems ( ARG_MAX) für so viele Dateien wie möglich gleichzeitig auszuführen . Die Effizienz des {} +Formulars beruht auf der Minimierung der Häufigkeit, mit der der ...Befehl aufgerufen werden muss, indem die Anzahl der Dateien maximiert wird, die an jeden Aufruf von übergeben werden ....
  • grep -l word {}: wobei das {}gleiche {}von oben wiederholt wird und durch Dateinamen ersetzt wird. Wie zuvor erläutert, werden grep -ldie Namen von Dateien gedruckt, die eine Übereinstimmung für enthalten word.
  • 2>/dev/null: Fehlermeldungen ausblenden (technisch Standardfehler an das Schwarze Loch umleiten /dev/null). Dies ist aus ästhetischen und praktischen Gründen, da läuft findauf /wahrscheinlich in Unmengen von „Zugriff verweigert“ führt Nachrichten Sie nicht kümmern können für Dateien , die Sie nicht über die Berechtigung zum Lesen und Verzeichnisse haben keine Berechtigung zu überqueren.

Es gibt einige Probleme mit den Vorschlägen, die Sie erhalten und in Ihrer Frage veröffentlicht haben. Beide

grep word `find / -name \*.sh 2>/dev/null

und

find / -name "*.sh" 2>/dev/null | xargs grep word

Fehler bei Dateien mit Leerzeichen im Namen. Es ist am besten zu vermeiden, Dateinamen vollständig in die Befehlsersetzung einzufügen. Der erste hat das zusätzliche Problem, möglicherweise auf das ARG_MAX-Limit zu stoßen. Der zweite ist nahe an dem, was ich vorschlage, aber es gibt keinen guten Grund, ihn xargshier zu verwenden , ganz zu schweigen davon, dass für eine sichere und korrekte Verwendung xargsdie Portabilität für einige Nur-GNU-Optionen geopfert werden muss ( find -print0 | xargs -0).

jw013
quelle
Vielen Dank für die verständliche und gründliche Erklärung!
Paulrehkugler
9

Unter nicht eingebettetem Linux, Cygwin oder einem anderen System mit GNU grep , unter FreeBSD , unter NetBSD und OSX :

grep -r --include='*.sh' word .

Analysieren Sie nicht die Ausgabe vonls . Und verwenden Sie keine Befehlssubstitution für die Ausgabe von find, wie jw013 erklärt hat .

Gilles 'SO - hör auf böse zu sein'
quelle
1
Doh, ich fühle mich albern, weil ich das Rekursive vergessen habe grep.
jw013
2

Die Kombination von grepund findist in vielen Fällen ack( betterthangrep.com ):

ack [OPTION]... PATTERN [FILE]

Erwägen Sie für Ihr Beispiel die Verwendung

ack --shell word /

Anmerkungen

ack

  • sucht (standardmäßig) rekursiv, aber
  • ignoriert (Standard) Verzeichnisse von gemeinsamen Versionskontrollsystemen, zB .git, .hg, .svn, ...
  • Sie können Ihre Ergebnisse leicht eingrenzen, indem Sie Filter für gängige Dateitypen verwenden (siehe unten für unterschiedliche Dateinamenmuster).
  • hat eine grepähnliche Syntax und die gleichen / ähnlichen Argumente wie -ifür "Groß- / Kleinschreibung ignorieren" usw.
  • kann ack-grepauf Ihrem System aufgerufen werden (auf Debian-basierten Distributionen, wenn ich mich richtig erinnere)

Dateinamenmuster

Die Option --shellist die Abkürzung für --type=shellund umfasst mehrere Dateitypen: derzeit .sh .bash .csh .tcsh .ksh .zshgemäß

ack --help-types

Wenn Sie nur .shDateien möchten , müssen Sie Ihren eigenen Typ definieren (hinzufügen) shund diesen Filter ( --sh) wie folgt verwenden:

ack word --type-add=sh=.sh --sh /

Dies klingt etwas kompliziert, ermöglicht jedoch die rekursive Suche nach .shDateien unten /. Für eine lokale Suche (ohne Angabe des Startverzeichnisses, z. B. \) wäre es einfacher:

ack word *.sh
fheub
quelle
Danke, dass du ackes mir gezeigt hast , ich wusste nie, dass es existiert, sehr praktisch für einen Entwickler!
Legends2k