¿Cómo buscar recursivamente el contenido del archivo / grep en un directorio / subdirectorios en Emacs?

14

Estoy usando Emacs durante bastante tiempo y todavía de alguna manera no entiendo cuál es la mejor manera de grep (recursivamente) para una cadena dentro de un directorio (proyecto) y obtener los resultados presentados como un búfer directo o en algunos más manera útil Por favor iluminame.

Boris Stitnicky
fuente
¿Has probado usando el helm-projectile-grepcomando (si tienes instalado el proyectil helm) o projectile-grep?
Chakravarthy Raghunandan
No sé qué helmo qué projectilees, pero si es una herramienta recomendada, la instalaría y la aprendería.
Boris Stitnicky
Sí, proyectil es un paquete que proporciona una manera fácil de hacer lo que mencionó en la pregunta con el comando projectile-grep. Además, le recomiendo que instale el helmpaquete. No me puedo imaginar ejecutar emacs sin helmy helm-projectile. También puede ser interesante en el helm-agpaquete (que proporciona una interfaz de timón para el buscador de plata en emacs, que supuestamente es más rápido que grep o ack).
Chakravarthy Raghunandan
2
Para que la pregunta sea más útil y más fácil de encontrar para los futuros buscadores de Google y foros, tal vez considere modificar el título de la pregunta para incluir la palabra de forma recursiva , y considere limitar el alcance, por ejemplo, Cómo agrupar recursivamente el contenido de un archivo en un directorio / subdirectorios . Eso es solo un ejemplo: podría ser otra cosa.
leyes
1
Si bien los comentarios y las respuestas hasta ahora han sugerido una serie de paquetes para hacer esto de varias maneras elegantes, no veo en su pregunta por qué simple M-x grepno hace lo que necesita. Te permite dar una línea de comando arbitraria, a veces la uso findallí cuando necesito la recursividad.
MAPA

Respuestas:

15

Bueno, como la pregunta original no menciona rgrep, seguiré y lo señalaré aquí. Esta es la opción más simple ya integrada en Emacs, y he encontrado que es más que suficiente para la mayoría de las cosas.

De la documentación,

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

La búsqueda se limita a los nombres de archivo que coinciden con el patrón de shell FILES.

Así, por ejemplo, para buscar todos los archivos pitón debajo de mi directorio principal para los usos de some_function, yo diría que es con some_function, *.py, ~/como los argumentos. Los resultados se muestran en un nuevo búfer.

usuario2699
fuente
1
Es posible que desee mencionar find-grep-diredtambién, ya que OP mencionó "resultados presentados como un búfer directo o ..."
npostavs
@npostavs No me he usado find-grep-dired, siéntase libre de agregarlo a la respuesta.
user2699
Una vez experimenté con los gustos de ack y ag, suponiendo que debería cambiar de rgrep a algo que los usara, porque se suponía que eran mejores. Al final me quedé con rgrep, ¡porque no encontré ningún beneficio en cambiar! En la línea de comando ciertamente hay un caso por hacer, pero en Emacs rgrephace un buen trabajo al apuntar a la búsqueda que no hubo diferencias significativas de velocidad entre ellos. (Quiero decir que rgrep era marginalmente más rápido en algunos casos, pero no lo recuerdo con certeza.)
PHILS
1
Tenga en cuenta que next-errory previous-errorse puede utilizar para navegar por el conjunto de resultados. Encuentro M-g M-ny M-g M-pel más conveniente de los enlaces estándar.
phils
find-grep-diredno me da un búfer con enlaces de referencias cruzadas. rgreplo hace por mí y esa última sugerencia sobre next-error previous-errores fantástica! Mi única objeción es que todos los resultados de ejecución de comandos desordenados al comienzo del búfer.
robert
11

Felizmente uso M-x find-grep-diredpor años. Devuelve los resultados como un búfer dired, que podría usar.

Michael Albinus
fuente
He tenido algunas dificultades para lograr que la vinculación de referencias cruzadas funcione con esto. ¡Su configuración es bastante desagradable ( find-ls-option) y, después de todo, termina con algo bastante detallado porque no funciona sin tener la fecha antes del nombre de archivo!
robert
5

Solución 1 (mejor solución):

Instalar consejo ( https://github.com/abo-abo/swiper/blob/master/counsel.el )

Entonces M-x counsel-git-grep.

No se necesita configuración (git conoce la raíz del proyecto y los archivos a excluir). Ambos git grepy counseles eficiente.

El proyecto debe ser gestionado por git.

El consejo requiere el modo hiedra.

Solución 2:

Esta solución utiliza grep y funciona en cualquier proyecto. Es inferior a la Solución 1 porque es más lenta y necesita una configuración manual. También se basa en el modo de hiedra.

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

Necesita crear .dir-locals.el para configurar simple-project-root, consulte https://www.gnu.org/software/emacs/manual/html_node/emacs/Directory-Variables.html para obtener detalles técnicos

El código en la solución 2 es solo un prototipo. Mi implementación real es mucho más compleja. Ver counsel-etags-grepen https://github.com/redguardtoo/counsel-etags/blob/master/counsel-etags.el

Resumen:

Esas son las dos mejores soluciones que conozco.

Si existen otras soluciones mejores, necesitan al menos resolver los siguientes problemas para estar listos para la producción,

  • cómo obtener la palabra clave para grep (por ejemplo, obtener la palabra clave de la región seleccionada)

  • escapar de la palabra clave

  • si existe un programa grep más eficiente, deberíamos usarlo (ripgrep, the_silver_searcher / ag, ... etc), o bien recurrir al grep predeterminado

  • la ventana candidata debe usar el ancho completo de la pantalla y los usuarios pueden filtrar candidatos de forma interactiva (es por eso que las personas usan hiedra o timón)

  • debemos mostrar la ruta relativa en la ventana candidata

  • capaz de reutilizar el resultado anterior de grepped. Por lo tanto, el resultado anterior debe guardarse. Puedes usar ivy-resumedesde ivyo helm-resumedesdehelm

  • Cuando guarde el resultado grepizado anterior, también se debe guardar el contexto del resultado anterior. Por ejemplo, en el código de la Solución 2. default-directoryes el contexto. Consulte https://github.com/abo-abo/swiper/issues/591 para obtener más detalles.

  • La expresión regular extendida se debe usar porque es más simple y ya la usa counsel-git-grepthe_silver_searcher / ag.

Chen Bin
fuente
4

Me gustaría agregar otra solución a la solución de @chen bin que también usa counsel(pero no necesita tener un proyecto mantenido gitpara esto).

Necesita instalar ag (el buscador plateado) y counselproporciona el comando counsel-agque busca en el directorio actual la cadena ingresada.

Si desea buscar dentro de un proyecto (no solo el directorio de trabajo actual), agregue esto a su .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)))

Esta función se usará agpara buscar recursivamente el directorio raíz del proyecto.

Editar : la función anterior se predetermina al símbolo en el punto de búsqueda. Si no le gusta ese comportamiento, reemplácelo (thing-at-point 'symbol)con nil. Y si quieres los resultados de búsqueda en su propio búfer, presionaC-c C-o

Edit2 : si has ripgrepinstalado, se puede reemplazar counsel-agcon counsel-rgla función anterior y va igual de bien :)

Chakravarthy Raghunandan
fuente
¿Es projectile-project-rootutilizable para encontrar la raíz del proyecto Rails?
Boris Stitnicky
Sí, siempre y cuando sea un proyecto de proyectil válido, funcionará. Había un paquete llamado proyectil-rieles, creo.
Chakravarthy Raghunandan
¿Por qué nombraste la función rag/...?
Boris Stitnicky
Es un estilo común que la gente usa a menudo cuando escribe sus propias funciones (puede ver que muchos otros lo hacen si navega por su configuración de emacs). Todas las funciones que definí comienzan con un rag/prefijo y eso las hace fáciles de encontrar en M-x:)
Chakravarthy Raghunandan
Ic, ese es tu nombre :-)
Boris Stitnicky
2

Aquí hay un ejemplo de una función grep personalizada que greps recursivamente el contenido del archivo (usando una expresión regular para el término de búsqueda) en un directorio y sus subdirectorios, y muestra los resultados con líneas de contexto antes y después. El incorporado compile.ely las grep.elbibliotecas proporcionan varios métodos para saltar a la ubicación en el archivo que contiene los resultados de búsqueda. No es necesario instalar nada más para que este ejemplo funcione: todo lo que se necesita es una versión completa de Emacs y una computadora que tenga grepinstalada la utilidad. La variable grep-programse puede ajustar para apuntar a una ubicación particular de la greputilidad si el valor predeterminado no es suficiente.

(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)))
lista de leyes
fuente
Si bien la pregunta no busca cómo modificar simultáneamente varios archivos que se devuelven como resultado de la función grep anterior, encuentro que es muy útil usar multiple-cursorsy wgrep. Hace que la modificación de múltiples archivos de una sola vez sea muy fácil. Sin embargo, dichas bibliotecas tendrían que instalarse si se desean esas características.
leyes
¿Cuál es la ventaja de usar esto sobre el incorporado rgrep?
npostavs
1
@npostavs: descubrí que la función integrada rgreplleva mucho tiempo personalizarla según mis criterios de búsqueda de expresiones regulares preferidos, y elegí codificar todo en una función personalizada (que uso varias veces al día). Si desea escribir una respuesta alternativa que describa cómo personalizarla rgrep, sería útil para otros lectores del foro en el futuro.
ley