Ich habe ein Verzeichnis (zB abc/def/efg
) mit vielen Unterverzeichnissen (zB:) abc/def/efg/(1..300)
. Alle diese Unterverzeichnisse haben eine gemeinsame Datei (zB file.txt
). Ich möchte eine Zeichenfolge nur in dieser mit file.txt
Ausnahme anderer Dateien suchen . Wie kann ich das machen?
Ich habe verwendet grep -arin "pattern" *
, aber es ist sehr langsam, wenn wir viele Unterverzeichnisse und Dateien haben.
command-line
grep
find
Rajesh Keladimath
quelle
quelle
Antworten:
Im übergeordneten Verzeichnis können Sie nur die folgenden Dateien verwenden
find
und ausführengrep
:quelle
-H
zu übergeben ,grep
damit in Fällen, in denen nur ein Pfad übergeben wird, dieser Pfad weiterhin gedruckt wird (und nicht nur die übereinstimmenden Zeilen aus der Datei).Sie könnten auch Globstar verwenden.
Das Erstellen von
grep
Befehlen mitfind
, wie in Zannas Antwort , ist eine äußerst robuste, vielseitige und tragbare Möglichkeit, dies zu tun (siehe auch die Antwort von sudodus ). Und muru hat einen ausgezeichneten Ansatz für die Verwendunggrep
der--include
Option veröffentlicht . Wenn Sie jedoch nur dengrep
Befehl und Ihre Shell verwenden möchten , gibt es eine andere Möglichkeit: Sie können die Shell selbst dazu bringen , die erforderliche Rekursion durchzuführen :Das
-H
Flaggrep
zeigt den Dateinamen an, auch wenn nur eine passende Datei gefunden wird. Sie können den Pass-a
,-i
und-n
Fahnen (von Ihrem Beispiel)grep
als auch, wenn es das ist , was Sie brauchen. Aber nicht bestanden-r
oder-R
bei dieser Methode. Es ist die Shell , die Verzeichnisse beim Erweitern des Glob-Musters rekursiv verwendet**
und nichtgrep
.Diese Anweisungen gelten speziell für die Bash-Shell. Bash ist die Standardbenutzershell unter Ubuntu (und den meisten anderen GNU / Linux-Betriebssystemen). Wenn Sie also unter Ubuntu arbeiten und Ihre Shell nicht kennen, ist dies mit ziemlicher Sicherheit Bash. Beliebte Shells unterstützen zwar normalerweise Verzeichnis-
**
übergreifende Globs, funktionieren jedoch nicht immer auf die gleiche Weise. Weitere Informationen finden Sie Stéphane Chazelas ‚s ausgezeichnete Antwort auf Das Ergebnis der ls *, ls ** und ls *** auf Unix.SE .Wie es funktioniert
Einschalten der globstar bash Shell Option macht
**
Spiel Pfade Verzeichnistrenner enthält (/
). Es ist also ein verzeichnisrekursiver Glob. Im Einzelnen, wieman bash
erklärt:Sie sollten vorsichtig sein, da Sie Befehle ausführen können, mit denen weit mehr Dateien geändert oder gelöscht werden, als Sie beabsichtigen, insbesondere, wenn Sie schreiben,
**
wann Sie schreiben möchten*
. (Es ist sicher in diesem Befehl, der keine iles ändert.)shopt -u globstar
Schaltet die Globstar-Shell-Option wieder aus.Es gibt einige praktische Unterschiede zwischen globstar und
find
.find
ist viel vielseitiger als globstar. Alles, was Sie mit globstar tun können, können Sie auch mit demfind
Befehl tun . Ich mag Globstar und manchmal ist es praktischer, aber Globstar ist keine generelle Alternative zufind
.Die obige Methode sucht nicht in Verzeichnissen, deren Namen mit a beginnen
.
. Manchmal möchten Sie solche Ordner nicht wiederverwenden, manchmal jedoch.Wie bei einem normalen Glob erstellt die Shell eine Liste aller übereinstimmenden Pfade und übergibt sie
grep
anstelle des Glob als Argumente an Ihr Kommando ( ). Wenn Sie so viele Dateien aufgerufen haben,file.txt
dass der resultierende Befehl für die Ausführung durch das System zu lang wäre, schlägt die oben beschriebene Methode fehl. In der Praxis würden Sie (mindestens) Tausende solcher Dateien benötigen, aber es könnte passieren.Die verwendeten Methoden
find
unterliegen nicht dieser Einschränkung, da:Zannas Methode erstellt und führt einen
grep
Befehl mit möglicherweise vielen Pfadargumenten aus. Wenn jedoch mehr Dateien gefunden werden, als in einem einzelnen Pfad aufgeführt werden können, führt die Aktion+
-terminated-exec
den Befehl mit einigen Pfaden aus und führt ihn dann erneut mit einigen weiteren Pfaden aus. Im Fallegrep
einer Zeichenfolge in mehreren Dateien führt dies zum korrekten Verhalten.Wie bei der hier beschriebenen Globstar-Methode werden alle übereinstimmenden Zeilen mit vorangestellten Pfaden gedruckt.
der weg von sudodus verläuft
grep
für jedesfile.txt
gefundene separat . Wenn es viele Dateien gibt, ist es möglicherweise langsamer als einige andere Methoden, aber es funktioniert.Bei dieser Methode werden Dateien gesucht und ihre Pfade gedruckt, gefolgt von etwaigen übereinstimmenden Zeilen. Dies ist ein anderes Ausgabeformat als das von meiner Methode, Zanna und Murus , erzeugte .
Farbe bekommen mit
find
Einer der unmittelbaren Vorteile von Globstar ist, dass Ubuntu standardmäßig
grep
kolorierte Ausgaben erzeugt. Aber man kann dies leicht zu bekommenfind
, auch .Benutzerkonten in Ubuntu werden mit einem Alias erstellt , der
grep
wirklich zum Laufen bringtgrep --color=auto
(runalias grep
to see). Es ist eine gute Sache, dass Aliase so gut wie nur dann erweitert werden, wenn Sie sie interaktiv ausgeben. Wenn Siefind
jedochgrep
mit dem--color
Flag aufrufen möchten , müssen Sie es explizit schreiben. Beispielsweise:quelle
bash
Shell verwenden müssen, damit dies funktioniert. Sie haben zu sagen , dass es implizit in „der globstar Bash - Shell - Option“ , aber es kann leicht von Menschen lesen zu schnell übersehen werden.**
Globs unterstützen, ist Ihre Kernkritik korrekt: Die Darstellung**
in dieser Antwort ist spezifisch für bash, wobei shopt nur bash und der Begriff "globstar" (glaube ich) bash und ist nur tcsh. Ich hatte das ursprünglich wegen dieser Komplexität beschönigt, aber Sie haben Recht, dass es etwas verwirrend ist. Anstatt es in dieser Antwort ausführlich zu diskutieren, habe ich auf einen anderen (ziemlich gründlichen) Post verwiesen, der das schwere Heben übernimmt.-e
sollte nicht auf Pfade angewendet werden, aber dies ist leicht zu beheben. Für den ersten Befehl einfach weglassen-e
. Verwenden Sie für die Sekundefind . -name file.txt -printf $'\e[32m%p:\e[0m\n' -exec grep -i "pattern" {} \;
oderfind . -name file.txt -exec printf '\e[32m%s:\e[0m\n' {} \; -exec grep -i "pattern" {} \;
. Benutzer bevorzugen manchmal Ihren Weg (mit-e
fester Verwendung) gegenüber den anderen, die einen Pfad pro übereinstimmender Zeile drucken . Ihr druckt einen Pfad pro gefundener Datei, gefolgt von dengrep
Ergebnissen.grep
selbst macht nicht was du machst. Einige andere Kritikpunkte waren auch falsch.grep -H
Laufen durch-exec
wird nicht ohne--color
(oderGREP_COLOR
) färben . 1.003,1-2.008 IEEE nicht garantieren{}
expandiert in##### {}:
, aber Ubuntu hat GNU find, was tut . Wenn es für Sie in Ordnung ist, bearbeite ich Ihren Beitrag, um den-e
Fehler zu beheben (und den Anwendungsfall zu klären), und Sie können sehen, ob Sie den Löschvorgang rückgängig machen möchten. (Ich habe den Repräsentanten, um gelöschte Beiträge anzuzeigen / zu bearbeiten.)Das brauchst du nicht
find
;grep
kann dies vollkommen alleine bewältigen:Von
man grep
:quelle
find?
Die in der Antwort von muru angegebene Methode ,
grep
mit dem--include
Flag einen Dateinamen anzugeben, ist häufig die beste Wahl. Dies kann jedoch auch mit erfolgenfind
.Der Ansatz in dieser Antwort Anwendungen
find
ausgeführt werdengrep
separat für jede Datei gefunden, und druckt den Pfad zu jeder Datei genau einmal , über den passenden in jeder Datei gefunden Zeilen. (Methoden, die den Pfad vor jeder übereinstimmenden Zeile ausgeben, werden in anderen Antworten behandelt.)Sie können das Verzeichnis in den oberen Bereich des Verzeichnisbaums verschieben, in dem sich diese Dateien befinden. Dann renne:
Das gibt den Pfad (relativ zum aktuellen Verzeichnis
.
und einschließlich des Dateinamens selbst) jeder genannten Datei ausfile.txt
, gefolgt von allen übereinstimmenden Zeilen in der Datei. Dies funktioniert, weil{}
ein Platzhalter für die Datei gefunden wird. Der Pfad jeder Datei unterscheidet sich von ihrem Inhalt durch das Präfix#####
und wird nur einmal vor den übereinstimmenden Zeilen aus dieser Datei gedruckt. (Aufgerufene Dateienfile.txt
, die keine Übereinstimmungen enthalten, werden immer noch mit Pfaden gedruckt.) Diese Ausgabe ist möglicherweise übersichtlicher als die Ausgabe von Methoden, die am Anfang jeder übereinstimmenden Zeile einen Pfad ausgeben.Eine
find
solche Verwendung ist fast immer schneller als die Ausführunggrep
für jede Datei (grep -arin "pattern" *
), dafind
nach Dateien mit dem richtigen Namen gesucht wird und alle anderen Dateien übersprungen werden.Ubuntu benutzt GNU find , das sich immer erweitert,
{}
auch wenn es in einer größeren Zeichenkette vorkommt , wie##### {}:
. Wenn Sie Ihren Befehl für die Arbeit mitfind
Systemen benötigen , die dies möglicherweise nicht unterstützen , oder die-exec
Aktion nur dann verwenden möchten, wenn dies unbedingt erforderlich ist, können Sie Folgendes verwenden:Um die Ausgabe besser lesbar zu machen , können Sie ANSI-Escape-Sequenzen verwenden, um farbige Dateinamen zu erhalten. Dadurch hebt sich die Pfadüberschrift jeder Datei besser von den übereinstimmenden Zeilen ab, die darunter gedruckt werden:
Das veranlasst Ihre Shell , den Escape-Code für Grün in die tatsächliche Escape-Sequenz umzuwandeln, die in einem Terminal Grün erzeugt, und dasselbe mit dem Escape-Code für normale Farbe zu tun. Diese Escapezeichen werden an übergeben
find
, das sie verwendet, wenn ein Dateiname gedruckt wird. ($'
'
Anführungszeichen sind hier erforderlich, dafind
die-printf
Aktion\e
für die Interpretation von ANSI-Escape-Codes nicht erkannt wird .)Wenn Sie möchten, können Sie stattdessen
-exec
mit demprintf
Systembefehl (das tut Unterstützung\e
). Eine andere Möglichkeit, dasselbe zu tun, ist:quelle
find abc/def/efg -name "file.txt" -type f -exec echo -e "##### {}:" \; -exec grep -i "pattern" {} \;
cd abc/def/efg
Befehl 'change directory' eliminieren :-)-e
Option anecho
? Dadurch werden alle Dateinamen, die umgekehrte Schrägstriche enthalten, unleserlich. (2) Es kann nicht garantiert werden, dass die Verwendung{}
als Teil eines Arguments funktioniert. Es wäre besser zu sagen-exec echo "#####" {} \;
oder-exec printf "##### %s:\n" {} \;
. (3) Warum nicht einfach-print
oder verwenden-printf
? (4) Betrachten Sie auchgrep -H
.find . -name "file.txt" -type f -exec echo -e "\0033[32m{}:\0033[0m" \; -exec grep -i "pattern" {} \;
2) Sie haben vielleicht Recht, aber bisher funktioniert dies bei mir. 3) -print und -printf sind ebenfalls Alternativen. 4) Dies ist bereits in der Hauptantwort enthalten. - Wie auch immer, Sie sind mit Ihrer eigenen Antwort willkommen :-)-exec
Anrufe nicht. Verwenden Sie einfachgrep -H
und das wird den Dateinamen (in Farbe) sowie den passenden Text drucken.Um darauf hinzuweisen, dass Sie direkt grep verwenden können, wenn die Bedingungen der Frage literarisch sind:
oder
quelle