Comment rechercher récursivement le contenu d'un fichier / grep dans un répertoire / sous-répertoires dans Emacs?

14

J'utilise Emacs depuis un certain temps et je n'arrive toujours pas à comprendre quelle est la meilleure façon de greper (récursivement) une chaîne dans un répertoire (projet) et d'obtenir les résultats présentés soit sous forme de tampon dired, soit dans certains encore plus manière utile. Veuillez m'éclairer.

Boris Stitnicky
la source
Avez-vous essayé d'utiliser la helm-projectile-grepcommande (si vous avez installé un projectile de barre) ou projectile-grep?
Chakravarthy Raghunandan
Je ne sais pas ce helmou projectileest, mais si elle est un outil recommandé, j'installerait et apprendre.
Boris Stitnicky
Oui, projectile est un package qui fournit un moyen facile de faire ce que vous avez mentionné dans la question avec la commande projectile-grep. En outre, je vous recommande fortement d'installer le helmpackage. Je ne peux pas imaginer exécuter emacs sans helmet helm-projectile. Vous pourriez également être intéressant dans le helm-agpackage (qui fournit une interface de barre pour le chercheur d'argent dans emacs, qui est censé être plus rapide que grep ou ack).
Chakravarthy Raghunandan
2
Pour que la question soit plus utile et plus facile à trouver pour les futurs chercheurs de Google et des forums, envisagez peut-être de modifier le titre de la question pour inclure le mot de manière récursive et envisagez de limiter la portée - par exemple, Comment récursivement récurer le contenu d'un fichier dans un répertoire / sous-répertoires . Ce n'est qu'un exemple - ce pourrait être autre chose à la place.
lawlist
1
Bien que les commentaires et les réponses aient jusqu'à présent suggéré un certain nombre de packages pour le faire de diverses manières, je ne vois pas dans votre question pourquoi le simple M-x grepne fait pas ce dont vous avez besoin. Cela vous permet de donner une ligne de commande arbitraire, je l'utilise parfois findlorsque j'ai besoin de la récursivité.
MAP

Réponses:

15

Eh bien, puisque la question d'origine ne mentionne pas rgrep, je vais aller de l'avant et le signaler ici. C'est l'option la plus simple déjà intégrée à Emacs, et je l'ai trouvée plus que suffisante pour la plupart des choses.

De la documentation,

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

La recherche est limitée aux noms de fichiers correspondant au fichier shell.

Ainsi, par exemple, pour rechercher tous les fichiers python sous mon répertoire pour des utilisations de some_function, je l'appellerais avec some_function, *.py, ~/comme les arguments. Les résultats sont affichés dans un nouveau tampon.

user2699
la source
1
Pourrait aussi être mentionné find-grep-diredcar OP a mentionné "les résultats présentés sous forme de tampon dirigé ou ..."
npostavs
@npostavs Je ne find-grep-diredme suis pas utilisé , n'hésitez pas à l'ajouter à la réponse.
user2699
J'ai expérimenté une fois avec les ack et ag, en supposant que je devrais passer de rgrep à quelque chose en utilisant ceux-ci, car ils étaient censés être meilleurs. Au final, je suis resté avec rgrep, car je n'ai trouvé aucun avantage à changer! Sur la ligne de commande, il y a certainement un argument à faire, mais dans Emacs rgrepfait un si bon travail de ciblage de la recherche qu'il n'y avait pas de différences de vitesse significatives entre eux. (Je veux dire que rgrep a été légèrement plus rapide dans certains cas, mais je ne m'en souviens pas avec certitude.)
phils
1
Notez que next-erroret previous-errorpeut être utilisé pour naviguer dans l'ensemble de résultats. Je trouve M-g M-net M-g M-pla plus pratique des fixations standard.
phils
find-grep-diredne me donne pas de tampon avec des liens de références croisées. rgreple fait pour moi et cette dernière suggestion next-error previous-errorest fantastique! Mon seul reproche, c'est que toutes les sorties d'exécution de commandes en désordre au début du tampon.
robert
11

J'utilise avec plaisir M-x find-grep-direddepuis des années. Il renvoie les résultats sous forme de tampon dirigé, que vous pouvez utiliser.

Michael Albinus
la source
J'ai eu quelques difficultés à obtenir la liaison croisée pour travailler avec cela. Sa configuration est assez désagréable ( find-ls-option) et après tout, vous vous retrouvez avec quelque chose de très verbeux car cela ne fonctionne pas sans avoir la date avant le nom de fichier!
robert
5

Solution 1 (meilleure solution):

Installer l'avocat ( https://github.com/abo-abo/swiper/blob/master/counsel.el )

Alors M-x counsel-git-grep.

Aucune configuration requise (git connaît la racine du projet et les fichiers à exclure). Les deux git grepet counselest efficace.

Le projet doit être géré par git.

l'avocat requiert le mode lierre.

Solution 2:

Cette solution utilise grep et fonctionne sur n'importe quel projet. Elle est inférieure à la Solution 1 car elle est plus lente et nécessite une configuration manuelle. Il est également basé sur le mode lierre.

(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))))))))

Vous devez créer .dir-locals.el pour l'installation simple-project-root, voir https://www.gnu.org/software/emacs/manual/html_node/emacs/Directory-Variables.html pour les détails techniques

Le code de la solution 2 n'est qu'un prototype. Ma véritable implémentation est beaucoup plus complexe. Voir counsel-etags-grepdans https://github.com/redguardtoo/counsel-etags/blob/master/counsel-etags.el

Sommaire:

Ce sont les deux meilleures solutions que je connaisse.

S'il existe d'autres meilleures solutions, elles doivent au moins résoudre les problèmes ci-dessous pour être prêtes pour la production,

  • comment obtenir le mot clé à grep (par exemple, obtenir le mot clé de la région sélectionnée)

  • échapper au mot-clé

  • s'il existe un programme grep plus efficace, nous devrions l'utiliser (ripgrep, the_silver_searcher / ag, ... etc), ou bien utiliser le grep par défaut

  • la fenêtre des candidats doit utiliser toute la largeur de l'écran et les utilisateurs peuvent filtrer les candidats de manière interactive (c'est pourquoi les gens utilisent du lierre ou du casque)

  • nous devons montrer le chemin relatif dans la fenêtre candidate

  • capable de réutiliser le résultat grepped précédent. Le résultat précédent doit donc être enregistré. Vous pouvez utiliser ivy-resumedepuis ivyou helm-resumedepuishelm

  • Lorsque vous enregistrez le résultat précédent, le contexte du résultat précédent doit également être enregistré. Par exemple, dans le code de la Solution 2. default-directoryest le contexte. Voir https://github.com/abo-abo/swiper/issues/591 pour plus de détails.

  • L'expression régulière étendue doit être utilisée car elle est plus simple et est déjà utilisée par counsel-git-grepthe_silver_searcher / ag.

chen bin
la source
4

Je voudrais ajouter une autre solution à la solution de @chen bin qui utilise également counsel(mais vous n'avez pas besoin d'avoir un projet géré par gitpour cela).

Vous devez installer ag (le moteur de recherche Silver) et counselfournir la commande counsel-agqui recherche dans le répertoire en cours la chaîne entrée.

Si vous souhaitez rechercher dans un projet (pas seulement le répertoire de travail actuel), ajoutez ceci à votre .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)))

Cette fonction permettra agde rechercher récursivement le répertoire racine du projet.

Modifier : la fonction ci-dessus est par défaut le symbole au point de recherche. Si vous n'aimez pas ce comportement, remplacez-le (thing-at-point 'symbol)par nil. Et si vous voulez les résultats de la recherche dans sa propre presse tamponC-c C-o

Edit2 : si vous avez ripgrepinstallé, vous pouvez remplacercounsel-ag par counsel-rgdans la fonction ci-dessus et ça ira aussi bien :)

Chakravarthy Raghunandan
la source
Est-il projectile-project-rootutilisable pour trouver la racine du projet Rails?
Boris Stitnicky
Oui, tant qu'il s'agit d'un projet de projectile valide, cela fonctionnera. Il y avait un paquet appelé projectile-rails je pense.
Chakravarthy Raghunandan
Pourquoi avez-vous nommé la fonction rag/...?
Boris Stitnicky
C'est un style commun que les gens utilisent souvent lorsqu'ils écrivent leurs propres fonctions (vous pouvez le voir être fait par beaucoup d'autres si vous parcourez leur configuration emacs). Toutes les fonctions que j'ai définies commencent par un rag/préfixe et cela les rend faciles à trouver sous M-x:)
Chakravarthy Raghunandan
Ic, c'est votre nom :-)
Boris Stitnicky
2

Voici un exemple de fonction grep personnalisée qui récupère récursivement le contenu des fichiers (en utilisant une expression rationnelle pour le terme de recherche) dans un répertoire et ses sous-répertoires, et affiche les résultats avec des lignes de contexte avant et après. La bibliothèque intégrée compile.elet les grep.elbibliothèques proposent plusieurs méthodes pour accéder à l'emplacement du fichier contenant les résultats de la recherche. Il n'est pas nécessaire d'installer quoi que ce soit d'autre pour que cet exemple fonctionne - il suffit d'une version complète d'Emacs et d'un ordinateur sur lequel l' greputilitaire est installé. La variable grep-programpeut être ajustée pour pointer vers un emplacement particulier de l' greputilitaire si la valeur par défaut n'est pas suffisante.

(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)))
liste des lois
la source
Bien que la question ne cherche pas à modifier simultanément plusieurs fichiers renvoyés en tant que résultats par la fonction grep ci-dessus, je trouve très utile d'utiliser multiple-cursorset wgrep. Il facilite la modification de plusieurs fichiers en un seul coup. Ces bibliothèques, cependant, devraient être installées si ces fonctionnalités sont souhaitées.
lawlist
Quel est l'avantage d'utiliser cela par rapport à la fonction intégrée rgrep?
npostavs
1
@npostavs - J'ai trouvé que la fonction intégrée prenait rgrepbeaucoup de temps à personnaliser selon mes critères de recherche d'expression régulière préférés, et j'ai choisi de tout coder en dur dans une fonction personnalisée (que j'utilise plusieurs fois par jour). Si vous souhaitez rédiger une autre réponse décrivant comment personnaliser rgrep, cela pourrait être utile à d'autres lecteurs du forum à l'avenir.
lawlist