Wie kann man rekursiv nach / grep-Dateiinhalten in einem Verzeichnis / Unterverzeichnissen in Emacs suchen?

14

Ich benutze Emacs seit geraumer Zeit und verstehe immer noch nicht, wie man (rekursiv) nach einem String in einem (Projekt-) Verzeichnis grept und die Ergebnisse entweder als Dired-Buffer oder in einem noch besseren anzeigt nützliche Weise. Bitte erleuchte mich.

Boris Stitnicky
quelle
Haben Sie das helm-projectile-grepKommando ausprobiert (wenn Sie ein Helmprojektil installiert haben) oder projectile-grep?
Chakravarthy Raghunandan
Ich weiß nicht was helmoder projectileist, aber wenn es ein empfohlenes Tool ist, würde ich es installieren und lernen.
Boris Stitnicky
Ja, projectile ist ein Paket, mit dem Sie auf einfache Weise das tun können, was Sie in der Frage mit dem Befehl angegeben haben projectile-grep. Außerdem empfehle ich Ihnen dringend, das helmPaket zu installieren . Ich kann mir nicht vorstellen, Emacs ohne helmund laufen zu lassen helm-projectile. Vielleicht interessiert Sie auch helm-agpackage (das eine Steuerschnittstelle für Silver Searcher in Emacs bietet, die angeblich schneller als grep oder ack ist).
Chakravarthy Raghunandan
2
Damit die Frage für zukünftige Google- und Forensucher nützlicher und leichter zu finden ist, sollten Sie möglicherweise den Titel der Frage so ändern, dass er das Wort rekursiv enthält , und den Umfang einschränken, z. B., wie Sie den Inhalt von Dateien in einem Verzeichnis rekursiv erfassen / Unterverzeichnisse . Das ist nur ein Beispiel - es könnte stattdessen etwas anderes sein.
Gesetzesliste
1
Während die bisherigen Kommentare und Antworten eine Reihe von Paketen vorgeschlagen haben, um dies auf unterschiedliche Weise zu tun, kann ich anhand Ihrer Frage nicht erkennen, warum simple M-x grepnicht das tut, was Sie benötigen. Es lässt sich eine beliebige Befehlszeile angeben, die ich dort manchmal verwende, findwenn ich die Rekursion benötige.
MAP

Antworten:

15

Nun, da die ursprüngliche Frage nicht erwähnt wird rgrep, werde ich hier darauf hinweisen. Dies ist die einfachste Option, die bereits in Emacs integriert ist, und ich habe festgestellt, dass sie für die meisten Dinge mehr als ausreichend ist.

Aus der Dokumentation,

Recursively grep for REGEXP in FILES in directory tree rooted at DIR.

Die Suche ist auf Dateinamen beschränkt, die dem Shell-Muster FILES entsprechen.

So zum Beispiel aller Python - Dateien unter meinem Home - Verzeichnis zu suchen , für die Verwendung von some_function, würde ich es nennt mit some_function, *.py, ~/wie die Argumente. Die Ergebnisse werden in einem neuen Puffer angezeigt.

user2699
quelle
1
find-grep-diredKönnte auch erwähnt werden, da OP erwähnte "Ergebnisse entweder als Dired Buffer oder ..."
Npostavs
@npostavs Ich habe find-grep-diredmich selbst nicht benutzt , zögern Sie nicht , es der Antwort hinzuzufügen.
User2699
Ich habe einmal mit Leuten wie ack und ag experimentiert, unter der Annahme, dass ich von rgrep zu etwas wechseln sollte, das diese verwendet, weil sie besser sein sollten. Am Ende bin ich bei rgrep geblieben, weil ich keinen Nutzen daraus gezogen habe, mich zu ändern! Auf der Kommandozeile ist sicherlich ein Fall zu klären, aber in Emacs rgrepgelingt die gezielte Suche so gut, dass es keine signifikanten Geschwindigkeitsunterschiede zwischen ihnen gab. (Ich möchte sagen, dass rgrep in einigen Fällen geringfügig schneller war , aber ich kann mich nicht sicher erinnern.)
phils
1
Beachten Sie, dass mit next-errorund previous-errordurch die Ergebnismenge navigiert werden kann. Ich finde M-g M-nund M-g M-pdie bequemste der Standardbindungen.
Phils
find-grep-diredGibt mir keinen Puffer mit Querverweislinks. rgreptut es für mich und dieser letzte Vorschlag next-error previous-errorist fantastisch! Mein einziges Manko ist, dass die gesamte Befehlsausführung zu Beginn des Puffers chaotisch ist.
Robert
11

Ich benutze glücklich M-x find-grep-diredseit Jahren. Es gibt die Ergebnisse als gerichteten Puffer zurück, den Sie verwenden können.

Michael Albinus
quelle
Ich hatte einige Schwierigkeiten, die Querverweisverknüpfung so zu gestalten, dass sie damit funktioniert. Die Konfiguration ist ziemlich unangenehm ( find-ls-option) und schließlich hat man etwas ziemlich Ausführliches, weil es nicht funktioniert, wenn das Datum nicht vor dem Dateinamen steht!
Robert
5

Lösung 1 (beste Lösung):

Installieren Sie counsel ( https://github.com/abo-abo/swiper/blob/master/counsel.el )

Dann M-x counsel-git-grep.

Kein Setup erforderlich (Git kennt den Projektstamm und die auszuschließenden Dateien). Beides git grepund counselist effizient.

Das Projekt muss von git verwaltet werden.

Für einen Rechtsbeistand ist ein Efeumodus erforderlich.

Lösung 2:

Diese Lösung verwendet grep und funktioniert in jedem Projekt. Es ist der Lösung 1 unterlegen, da es langsamer ist und manuell eingerichtet werden muss. Es basiert auch auf dem Efeu-Modus.

(defvar simple-project-root "~/.emacs.d")
(defun simple-grep ()
  (interactive)
  (unless (featurep 'ivy)
    (require 'ivy))
  (let* ((default-directory (file-name-as-directory simple-project-root))
         (keyword (read-string "Keyword:")))
    (ivy-read (format "Grep at %s:" simple-project-root)
              (split-string (shell-command-to-string (format "grep -rsnI %s ." keyword)) "\n" t)
              :action (lambda (val)
                        (let* ((lst (split-string val ":"))
                               (linenum (string-to-number (cadr lst))))
                          ;; open file
                          (find-file (car lst))
                          ;; goto line if line number exists
                          (when (and linenum (> linenum 0))
                            (goto-char (point-min))
                            (forward-line (1- linenum))))))))

Sie müssen zum Einrichten .dir-locals.el erstellen. Technische Details finden simple-project-rootSie unter https://www.gnu.org/software/emacs/manual/html_node/emacs/Directory-Variables.html

Der Code in Lösung 2 ist nur ein Prototyp. Meine eigentliche Implementierung ist viel komplexer. Siehe counsel-etags-grepunter https://github.com/redguardtoo/counsel-etags/blob/master/counsel-etags.el

Zusammenfassung:

Das sind die besten zwei Lösungen, die ich kenne.

Wenn es andere bessere Lösungen gibt, müssen diese zumindest die folgenden Probleme lösen, um produktionsbereit zu sein.

  • wie man das Schlüsselwort zu grep bringt (zum Beispiel ein Schlüsselwort aus einer ausgewählten Region holen)

  • Entkomme dem Schlüsselwort

  • Wenn ein effizienteres grep-Programm vorhanden ist, sollten wir es verwenden (ripgrep, the_silver_searcher / ag, ... etc) oder auf das Standard-grep zurückgreifen

  • Das Kandidatenfenster sollte die volle Bildschirmbreite nutzen und die Benutzer können die Kandidaten interaktiv filtern (daher wird Efeu oder Helm verwendet).

  • Wir sollten den relativen Pfad im Kandidatenfenster anzeigen

  • in der Lage, frühere grepped Ergebnis wiederzuverwenden. Das vorherige Ergebnis sollte also gespeichert werden. Sie können ivy-resumevon ivyoder helm-resumevon verwendenhelm

  • Wenn Sie das vorherige Grepped-Ergebnis speichern, sollte auch der Kontext des vorherigen Ergebnisses gespeichert werden. Zum Beispiel steht im Code von Lösung 2 der default-directoryKontext. Weitere Informationen finden Sie unter https://github.com/abo-abo/swiper/issues/591 .

  • Der erweiterte reguläre Ausdruck sollte verwendet werden, da er einfacher ist und bereits von counsel-git-grepund the_silver_searcher / ag verwendet wird.

chen bin
quelle
4

Ich möchte @chen bin's Lösung um eine weitere Lösung erweitern, die ebenfalls verwendet wird counsel(Sie müssen jedoch kein Projekt gitdafür verwalten).

Sie müssen ag (den Silver Searcher) installieren und counselden Befehl bereitstellen, der counsel-agdas aktuelle Verzeichnis nach der eingegebenen Zeichenfolge durchsucht.

Wenn Sie in einem Projekt suchen möchten (nicht nur im aktuellen Arbeitsverzeichnis), fügen Sie Folgendes hinzu .emacs:

  (defun rag/counsel-ag-project-at-point ()
    "use counsel ag to search for the word at point in the project"
    (interactive)
    (counsel-ag (thing-at-point 'symbol) (projectile-project-root)))

Diese Funktion wird agzum rekursiven Durchsuchen des Projektstammverzeichnisses verwendet.

Bearbeiten : Die obige Funktion verwendet standardmäßig das Symbol an der Stelle, an der gesucht werden soll. Wenn Ihnen dieses Verhalten nicht gefällt, ersetzen Sie es (thing-at-point 'symbol)durch nil. Und wenn Sie die Suchergebnisse in einem eigenen Puffer anzeigen möchten, drücken SieC-c C-o

Edit2 : wenn Sie schon ripgrepinstalliert haben , können Sie ersetzen counsel-agmit counsel-rgin der obigen Funktion und es wird nur so fein :)

Chakravarthy Raghunandan
quelle
Kann projectile-project-rootman Rails-Projektstamm finden?
Boris Stitnicky
Ja, solange es ein gültiges Projektilprojekt ist, wird es funktionieren. Ich glaube, es gab ein Paket namens Projektilschienen.
Chakravarthy Raghunandan
Warum haben Sie die Funktion benannt rag/...?
Boris Stitnicky
Es ist ein üblicher Stil, den die Leute häufig verwenden, wenn sie ihre eigenen Funktionen schreiben (Sie können sehen, dass dies von vielen anderen getan wird, wenn Sie ihre emacs-Konfiguration durchsuchen). Alle Funktionen, die ich definiert habe, beginnen mit einem rag/Präfix und sind daher leicht zu finden unter M-x:)
Chakravarthy Raghunandan
Ic, das ist dein Name :-)
Boris Stitnicky
2

Hier ist ein Beispiel für eine benutzerdefinierte grep-Funktion, mit der Dateiinhalte (unter Verwendung eines regulären Ausdrucks für den Suchbegriff) in einem Verzeichnis und seinen Unterverzeichnissen rekursiv überprüft und die Ergebnisse mit Kontextzeilen davor und danach angezeigt werden. Die integrierte Bibliothek compile.elund die grep.elBibliotheken bieten verschiedene Methoden, um zu der Position in der Datei zu springen, die die Suchergebnisse enthält. Für dieses Beispiel ist keine weitere Installation erforderlich. Sie benötigen lediglich eine Vollversion von Emacs und einen Computer, auf dem das grepDienstprogramm installiert ist. Die Variable grep-programkann so angepasst werden, dass sie auf eine bestimmte Position des grepDienstprogramms verweist, wenn die Standardeinstellung nicht ausreicht.

(defun recursive-grep ()
"Recursively grep file contents.  `i` case insensitive; `n` print line number;
`I` ignore binary files; `E` extended regular expressions; `r` recursive"
(interactive)
  (let* ((search-term (read-string "regex:  "))
         (search-path
           (directory-file-name (expand-file-name (read-directory-name "directory:  "))))
         (default-directory (file-name-as-directory search-path))
         (grep-command
           (concat
             grep-program
             " "
             "-inIEr --color=always -C2"
             " "
             search-term
             " "
             search-path)))
    (compilation-start grep-command 'grep-mode (lambda (mode) "*grep*") nil)))
Gesetzesliste
quelle
Während die Frage nicht danach sucht, wie mehrere Dateien, die von der obigen grep-Funktion als Ergebnis zurückgegeben werden, gleichzeitig geändert werden können, finde ich es sehr hilfreich, multiple-cursorsund zu verwenden wgrep. Es macht das Ändern mehrerer Dateien auf einen Schlag zum Kinderspiel. Diese Bibliotheken müssten jedoch installiert werden, wenn diese Funktionen gewünscht werden.
Gesetzesliste
Was ist der Vorteil gegenüber dem eingebauten rgrep?
Npostavs
1
@npostavs - Ich fand, dass das Anpassen des eingebauten rgrepCodes an meine bevorzugten Suchkriterien für reguläre Ausdrücke zeitaufwendig ist, und entschied mich dafür, alles in einer benutzerdefinierten Funktion (die ich mehrmals täglich verwende) fest zu codieren. Wenn Sie eine alternative Antwort verfassen möchten, in der das Anpassen beschrieben rgrepwird, ist dies in Zukunft für andere Forum-Leser hilfreich.
Gesetzesliste