Come cercare ricorsivamente / contenuti di file grep in una directory / sottodirectory in Emacs?


14

Sto usando Emacs da un po 'di tempo e ancora in qualche modo non riesco a capire quale sia il modo migliore per eseguire (in modo ricorsivo) grep per una stringa all'interno di una directory (di progetto) e ottenere i risultati presentati come un buffer diretto o in alcuni ancora di più modo utile. Per favore, illuminami.


Hai provato usando il helm-projectile-grepcomando (se hai installato il proiettile del timone) o projectile-grep?
Chakravarthy Raghunandan,

Non so cosa helmo cosa projectilesia, ma se è uno strumento consigliato, lo installerei e lo imparerei.
Boris Stitnicky,

Sì, il proiettile è un pacchetto che fornisce un modo semplice per fare ciò che hai menzionato nella domanda con il comando projectile-grep. Inoltre, consiglio vivamente di installare il helmpacchetto. Non riesco a immaginare di eseguire Emacs senza helme helm-projectile. Potresti anche essere interessante nel helm-agpacchetto (che fornisce un'interfaccia helm per il cercatore d'argento in emacs, che è presumibilmente più veloce di grep o ack).
Chakravarthy Raghunandan,

2
Affinché la domanda sia più utile e più facile da trovare per i futuri ricercatori di Google e forum, forse prendere in considerazione la modifica del titolo della domanda per includere la parola in modo ricorsivo e considerare la limitazione dell'ambito - ad esempio, Come ricorrere in modo ricorsivo al contenuto del file in una directory / sottodirectory . Questo è solo un esempio: potrebbe essere invece qualcos'altro.
elenco delle leggi

1
Mentre i commenti e le risposte finora hanno suggerito una serie di pacchetti per farlo in vari modi fantasiosi, non vedo dalla tua domanda perché semplice M-x grepnon fa quello che ti serve. Ti permette di dare una riga di comando arbitraria, a volte la uso findlì quando ho bisogno della ricorsione.
MAP

Risposte:


15

Bene, poiché la domanda originale non menziona rgrep, andrò avanti e lo indicherò qui. Questa è l'opzione più semplice già integrata in Emacs e l'ho trovata più che sufficiente per la maggior parte delle cose.

Dalla documentazione,

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

La ricerca è limitata ai nomi di file corrispondenti a FILES della shell.

Così, per esempio, per cercare tutti i file python sotto la mia home directory per usi some_function, lo chiamerei con some_function, *.py, ~/come gli argomenti. I risultati vengono visualizzati in un nuovo buffer.


1
Potrebbe voler essere menzionato find-grep-diredanche dal momento che OP ha menzionato "i risultati presentati come buffer
diretto

@npostavs Non ho usato find-grep-diredme stesso, sentiti libero di aggiungerlo alla risposta.
user2699

Una volta ho sperimentato cose del genere di ack e ag, supponendo che avrei dovuto passare da rgrep a qualcosa usando quelle, perché avrebbero dovuto essere migliori. Alla fine sono rimasto con rgrep, perché non ho trovato alcun vantaggio nel cambiare! Sulla riga di comando c'è sicuramente un caso da fare, ma in Emacs rgrepfa un ottimo lavoro nel mirare alla ricerca che non c'erano differenze di velocità significative tra di loro. (Voglio dire che rgrep in alcuni casi era leggermente più veloce , ma non ricordo per certo.)
phils

1
Si noti che next-errore previous-errorpuò essere utilizzato per navigare nel set di risultati. Trovo M-g M-ne M-g M-pil più conveniente degli attacchi standard.
phils,

find-grep-dirednon mi fornisce un buffer con collegamenti a riferimenti incrociati. rgreplo fa per me e l'ultimo suggerimento next-error previous-errorè fantastico! Il mio unico cavillo è che tutto l'output dell'esecuzione del comando disordinato all'inizio del buffer.
robert,

11

Uso felicemente M-x find-grep-diredda anni. Restituisce i risultati come buffer diretto, che è possibile utilizzare.


Ho avuto qualche difficoltà a far funzionare il collegamento incrociato con questo. La sua configurazione è piuttosto brutta ( find-ls-option) e dopo tutto si finisce con qualcosa di piuttosto prolisso perché non funziona senza avere la data prima del nome del file!
robert,

5

Soluzione 1 (migliore soluzione):

Consigli per l'installazione ( https://github.com/abo-abo/swiper/blob/master/counsel.el )

Poi M-x counsel-git-grep.

Nessuna installazione necessaria (git conosce la radice del progetto e i file da escludere). Entrambi git greped counselè efficiente.

Il progetto deve essere gestito da git.

il consiglio richiede la modalità edera.

Soluzione 2:

Questa soluzione utilizza grep e funziona su qualsiasi progetto. È inferiore alla Soluzione 1 perché è più lento e necessita di installazione manuale. Si basa anche sulla modalità edera.

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

È necessario creare .dir-locals.el per l'installazione simple-project-root, consultare https://www.gnu.org/software/emacs/manual/html_node/emacs/Directory-Variables.html per i dettagli tecnici

Il codice nella soluzione 2 è solo un prototipo. La mia vera implementazione è molto più complessa. Vedi counsel-etags-grepin https://github.com/redguardtoo/counsel-etags/blob/master/counsel-etags.el

Sommario:

Quelle sono le due migliori soluzioni che conosco.

Se esistono altre soluzioni migliori, devono almeno risolvere i problemi sottostanti per essere pronti per la produzione,

  • come ottenere la parola chiave in grep (ad esempio, ottenere la parola chiave dalla regione selezionata)

  • sfuggire alla parola chiave

  • se esiste un programma grep più efficiente, dovremmo usarlo (ripgrep, the_silver_searcher / ag, ... ecc), oppure fallback del grep predefinito

  • la finestra del candidato deve utilizzare l'intera larghezza dello schermo e gli utenti possono filtrare i candidati in modo interattivo (ecco perché le persone usano l'edera o il timone)

  • dovremmo mostrare il percorso relativo nella finestra candidata

  • in grado di riutilizzare il precedente risultato grepped. Quindi il risultato precedente dovrebbe essere salvato. Puoi usare ivy-resumeda ivyo helm-resumedahelm

  • Quando si salva il risultato precedente grepped, è necessario salvare anche il contesto del risultato precedente. Ad esempio, nel codice della soluzione 2. default-directoryè il contesto. Vedi https://github.com/abo-abo/swiper/issues/591 per maggiori dettagli.

  • L'espressione regolare estesa deve essere utilizzata perché è più semplice ed è già utilizzata da counsel-git-grepe the_silver_searcher / ag.


4

Vorrei aggiungere un'altra soluzione alla soluzione di @chen bin che utilizza anche counsel(ma per questo non è necessario avere un progetto gestito git).

Devi installare ag (il cercatore d'argento) e counselfornisce il comando counsel-agche cerca nella directory corrente la stringa inserita.

Se vuoi cercare all'interno di un progetto (non solo la directory di lavoro corrente), aggiungi questo al tuo .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)))

Questa funzione utilizzerà agper cercare ricorsivamente nella directory principale del progetto.

Modifica : la funzione di cui sopra si imposta automaticamente sul simbolo nel punto di ricerca. Se non ti piace quel comportamento, sostituiscilo (thing-at-point 'symbol)con nil. E se vuoi i risultati della ricerca nel suo buffer premiC-c C-o

Edit2 : se hai ripgrepinstallato, puoi sostituirlo counsel-agcon counsel-rgnella funzione sopra e andrà altrettanto bene :)


È projectile-project-rootutilizzabile per trovare la radice del progetto Rails?
Boris Stitnicky,

Sì, purché sia ​​un progetto proiettile valido funzionerà. Penso che ci fosse un pacchetto chiamato binari a proiettili.
Chakravarthy Raghunandan,

Perché hai chiamato la funzione rag/...?
Boris Stitnicky,

È uno stile comune che le persone usano spesso quando scrivono le proprie funzioni (puoi vederlo fatto da molti altri se navighi nella loro configurazione di emacs). Tutte le funzioni che ho definito iniziano con il rag/prefisso e questo le rende facilmente reperibili in M-x:)
Chakravarthy Raghunandan

Ic, questo è il tuo nome :-)
Boris Stitnicky l'

2

Ecco un esempio di una funzione grep personalizzata che greps ricorsivamente il contenuto del file (usando una regexp per il termine di ricerca) in una directory e nelle sue sottodirectory e visualizza i risultati con linee di contesto prima e dopo. Il built-in compile.ele le grep.ellibrerie prevedono diversi metodi per passare alla posizione nel file contenente i risultati della ricerca. Non è necessario installare nient'altro per far funzionare questo esempio: tutto ciò che serve è una versione completa di Emacs e un computer su cui sia grepinstallata l' utilità. La variabile grep-programpuò essere regolata per puntare a una posizione particolare grepdell'utilità se il valore predefinito non è sufficiente.

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

Sebbene la domanda non cerchi come modificare contemporaneamente più file che vengono restituiti come risultati dalla funzione grep sopra, trovo molto utile usare multiple-cursorse wgrep. Rende semplice la modifica di più file in un colpo solo. Dette librerie, tuttavia, dovrebbero essere installate se tali funzionalità sono desiderate.
elenco delle leggi

Qual è il vantaggio di utilizzare questo rispetto al builtin rgrep?
npostavs

1
@npostavs - Ho trovato che il built-in rgreprichiede molto tempo per personalizzare i miei criteri di ricerca regex preferiti e ho scelto di codificare tutto in una funzione personalizzata (che uso più volte al giorno). Se desideri scrivere una risposta alternativa che descriva come personalizzare rgrep, in futuro sarebbe utile per altri lettori di forum.
elenco delle leggi
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.