Uso de Emacs para buscar y reemplazar recursivamente en archivos de texto no abiertos


Como continuación de esta pregunta, está tratando de averiguar cómo hacer algo como esto que debería ser fácil, que especialmente me impide acostumbrarme más a usar Emacs y en su lugar iniciar el editor con el que ya estoy familiarizado. Utilizo el ejemplo aquí bastante a menudo en la edición de varios archivos.

En Ultraedit haría Alt + s luego p para mostrar un cuadro de diálogo con las opciones: Buscar (incluye el uso de expresiones regulares en varias líneas), Reemplazar con, En Archivos / Tipos, Directorio, Caso del Partido, Partido Palabra Entera Solamente, Lista Ficheros Cambiados y Subdirectorios de la búsqueda. Por lo general, primero usaré el ratón para hacer clic y arrastrar seleccione el texto que quiero reemplazar.

Usando solo el propio Emacs (en Windows XP), sin llamar a ninguna utilidad externa, cómo reemplazar todos los foo\nbar con bar\nbaz en los archivos *.c y *.h en alguna carpeta y todas las carpetas debajo de ella. Tal vez Emacs no es la mejor herramienta para hacer esto, pero ¿cómo se puede hacer fácilmente con un comando mínimo?

 191
Author: Community, 2008-11-07

13 answers

  1. M-x find-name-dired: se le pedirá un directorio raíz y un patrón de nombre de archivo.
  2. Presione t para "alternar marca" para todos los archivos encontrados.
  3. Presione Q para "Consulta-Reemplazar en archivos...": se le preguntará por expresiones regulares de consulta / sustitución.
  4. Proceda como query-replace-regexp: SPACE para reemplazar y pasar a la siguiente coincidencia, n para omitir una coincidencia, etc.
  5. Pulse C-x s para guardar los búferes. (A continuación, puede pulsar y, n o ! para guardar todos a la vez)
 336
Author: Chris Conway,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-09-12 12:42:28
  • M-x find-name-dired RET
    • puede tomar algún tiempo para que todos los archivos aparezcan en la lista, desplácese hasta la parte inferior (M->) hasta que aparezca "find finished" para asegurarse de que todos han cargado
  • Presione t para "alternar marca" para todos los archivos encontrados
  • Presione Q para "Consulta-Reemplazar en archivos...": se le preguntará por expresiones regulares de consulta / sustitución.
  • Proceda como con query-replace-regexp: SPACE o y para reemplazar y pasar a la siguiente coincidencia, n para omitir una coincidencia, sucesivamente.
    • Tipo ! para reemplazar todas las ocurrencias en el archivo actual sin preguntar, N para omitir todo posible reemplazo para el resto del archivo actual. (N es solo emacs 23+)
    • Para hacer el reemplazo en todos los archivos sin preguntar más, escriba Y.
  • Llame a "ibfer" (C-x C-b si está vinculado a ibfer, o M-x ibuffer RET) para listar todos los archivos abiertos.
  • Escriba * u para marcar todos los archivos no guardados, escriba S para guardar todos los archivos marcados
  • * * RET para desmarcar todas las marcas, o escriba D para cerrar todos los archivos marcados

Esta respuesta se combina de esta respuesta , de este sitio , y de mis propias notas. Usando Emacs 23+.

 34
Author: Frank Henard,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-05-23 12:10:33

Generalmente uso otras herramientas para realizar esta tarea, y parece que muchos de los enfoques mencionados en la entrada Buscar y Reemplazar a través de archivos de EmacsWiki shell out, pero el Paquete Findr parece muy prometedor.

Robando parte de el archivo fuente :

(defun findr-query-replace (from to name dir)
  "Do `query-replace-regexp' of FROM with TO, on each file found by findr.
 12
Author: Blair Conrad,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2008-11-07 01:52:04

Las respuestas proporcionadas son excelentes, sin embargo, pensé que agregaría un enfoque ligeramente diferente.

Es un método más interactivo, y requiere wgrep, rgrep y iedit. Tanto iedit como wgrep deben instalarse a través de MELPA o mermelada (utilizando M-x package-list-packages)

Primero ejecuta M-x rgrep para encontrar la cadena que estás buscando.

Podrá especificar los tipos de archivo/patrón y la carpeta a recurrir.

A continuación, tendrá que ejecutar wgrep iniciarlo con C-s C-p.

Wgrep will le permite editar los resultados de rgrep, así que establezca una región en la cadena para que coincida y comience iedit-mode con C-; (dependiendo de su terminal, puede necesitar volver a vincular esto)

Todas las ocurrencias serán editables a la vez. C-x C-s para confirmar wgrep. Luego C-x s ! para guardar los archivos cambiados.

El principal beneficio de este método es que puede usar iedit-mode para desactivar ciertas coincidencias M-;. También puede utilizar los resultados en rgrep para saltar a los archivos, por ejemplo, si tiene un inesperado coincidir.

Lo encuentro muy útil para hacer ediciones de código fuente y renombrar símbolos (variables, nombres de funciones, etc.).) a través de un proyecto.

Si aún no sabes/usas el modo iedit, es una herramienta muy útil, te recomiendo que le eches un vistazo.

 10
Author: ocodo,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-12-17 01:34:43

Usar dired para recurrir a un árbol de directorios profundos va a ser un poco lento para esta tarea. Podría considerar usar tags-query-replace. Esto significa desgranar para crear una tabla de etiquetas, pero a menudo es útil de todos modos, y es rápido.

 5
Author: Alex Coventry,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2008-11-07 12:56:02

Proyectil es realmente agradable: C-c p r ejecuta el comando proyectil-reemplazar

 5
Author: eflanigan00,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-12-02 19:00:30

Fuente de información: 1

Para los usuarios de emacs pro:

  1. Llame a dired para listar archivos en dir, o llame a find-dired si necesita todos los subdirectorios.
  2. Marque los archivos que desea. Puede marcar por regex escribiendo 【%m】.
  3. Escriba Q para llamar a dired-do-query-replace-regexp.
  4. Escriba la expresión regular find y la cadena replace. pattern patrón de regex elisp común {
  5. Para cada ocurrencia, escriba y para reemplazar, n para omitir. Escriba 【Ctrl + g】 para abortar todo operación.
  6. Tipo ! para reemplazar todas las ocurrencias en el archivo actual sin preguntar, N omita todo reemplazo posible para el resto del archivo actual. (N es emacs 23 solamente)
  7. Para hacer el reemplazo en todos los archivos sin preguntar más, escriba Y. (solo Emacs 23)
  8. Llame a ibuffer para listar todos los archivos abiertos. Escriba 【*u】 para marcar todos los archivos no guardados, escriba S para guardar todos los archivos marcados, escriba D para cerrarlos todos.

Guía paso a paso para Emacs Principiantes

Seleccionar archivos de destino

Inicie emacs escribiendo "emacs" en el símbolo del sistema de la interfaz de línea de comandos. (O, haga doble clic en el icono de Emacs si se encuentra en un entorno de interfaz de usuario de gráficos)

Seleccionar archivos en un directorio

Primero debe seleccionar los archivos que desea reemplazar. Utilice el menú gráfico File Archivo ▸ Abrir directorio Open. Emacs le pedirá una ruta de acceso al directorio. Escriba la ruta del directorio y, a continuación, pulse Intro.

Ahora, se le mostrará el lista de archivos, y ahora necesita marcar los archivos en los que desea que funcione la expresión regular find/replace. Marque un archivo moviendo el cursor al archivo que desee, luego presione m. Desmarque presionando u. (Para listar subdirectorios, mueva el cursor al directorio y presione i.El contenido del subdirectorio se mostrará en la parte inferior.) Para marcar todos los archivos con una expresión regular, escriba 【%m】 y, a continuación, escriba su patrón de expresiones regulares. Por ejemplo ,si desea marcar todos los archivos HTML, escriba 【%m】.HTML$. (Puede encontrar una lista de los comandos mark en el menú gráfico "Mark" (este menú aparece cuando está en el modo dired).)

Seleccionar Archivos en un Directorio y Todos sus Subdirectorios

Si desea buscar/reemplazar en archivos dentro de un directorio, incluyendo cientos de subdirectorios, aquí hay un método para seleccionar todos estos archivos.

Llama a find-dired. (se llama a un comando pulsando 【Alt + x】) A continuación, escriba un nombre de directorio, {/Users/mary / myfiles

Nota: si está utilizando emacs en un unix terminal de texto no gráfico, y si 【Alt + x】 no funciona, el trazo de tecla equivalente es 【Esc x】.

Emacs le preguntará con el mensaje "Ejecutar find (con args): ". Si necesita hacer el reemplazo en todos los archivos HTML, escriba el nombre "* html". Si no le importa qué tipo de archivo, sino simplemente todos los archivos bajo ese directorio, dé "-type f".

Ahora, marque los archivos como se describe anteriormente.

Búsqueda/Reemplazo interactivo

Ahora, ya está listo para hacer la búsqueda interactiva reemplazar. Para simplificar, digamos que solo quieres reemplazar la palabra "rápido"por " súper". Ahora, llama a dired-do-query-replace-regexp. Le pedirá la cadena regex y la cadena de reemplazo. Escriba "rápido", enter, luego"super".

Ahora, emacs usará su patrón y revisará los archivos, y se detendrá y le mostrará cada vez que haya una coincidencia. Cuando esto sucede, emacs le pedirá, y usted tiene la opción de hacer el cambio o omitir el cambio. Para realizar el cambio, escriba y. Para omitir, escriba n. Si simplemente desea que emacs siga adelante y realice todos estos cambios en el archivo actual, escriba !.

Si desea cancelar toda la operación sin guardar ningún cambio que haya realizado, escriba 【Ctrl+g】, luego salga de emacs utilizando el menú File Archivo ▸ Salir de Emacs..

Guardar los archivos modificados

Ahora, después de pasar por la prueba anterior, hay un paso más que debe hacer, y es guardar los archivos modificados.

Si está utilizando emacs versión 22 o posterior, entonces llame a ibfer para vaya a un modo de lista de búfer, luego escriba 【*u】 para marcar todos los archivos no guardados, luego escriba S para guardarlos todos. (eso es shift-s)

Si está utilizando una versión 21 de emacs, entonces puede hacer esto: llamar a list-buffers, luego mover el cursor al archivo que desea guardar y escribir s. Marcará el archivo para una acción de guardado posterior. Escriba u para desmarcar. Una vez que haya terminado, escriba x para ejecutar el guardado de todos los archivos marcados para guardar. (en emacs, el archivo abierto se llama "buffer". Ignorar otras cosas alli.)

Alternativa a las opciones anteriores, también puede llamar a save-some-buffers 【Ctrl+x s】. A continuación, emacs mostrará cada archivo no guardado y le preguntará si desea que se guarde.

Nota: la expresión regular de emacs no es la misma que la de Perl o Python, pero similar. Para un resumen y patrones comunes, vea: Regex de Emacs.

 4
Author: scrapcodes,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-12-24 06:45:01

M-X Dired, y t para marcar todos los archivos, y Q para buscar reemplazar texto en todos ellos. Puede expandir un subdirectorio usando el comando i antes de la consulta-reemplazar. La información clave que estoy agregando es que si le das un prefijo (control-u) al comando i, le pedirá arg, y el argumento-R expandirá recursivamente all subdirs en el búfer dired. Así que ahora usted puede consultar-buscar cada archivo en un directorio completo.

 3
Author: Zack Murray,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-10-22 20:59:55

Para los búferes abiertos, esto es lo que hago:

(defun px-query-replace-in-open-buffers (arg1 arg2)
  "query-replace in all open files"
  (interactive "sRegexp:\nsReplace with:")
  (mapcar
   (lambda (x)
     (find-file x)
     (save-excursion
       (goto-char (point-min))
       (query-replace-regexp arg1 arg2)))
   (delq
    nil
    (mapcar
     (lambda (x)
       (buffer-file-name x))
     (buffer-list)))))
 2
Author: yPhil,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2012-07-27 15:36:38

Otra opción es usar searchCles search. Este es un tipo diferente de búsqueda incremental que utiliza la finalización de su entrada de minibuffer contra los resultados de búsqueda. A medida que modifica su entrada actual, el conjunto de coincidencias se actualiza en buffer *Completions*.

Puede buscar cualquier número de archivos, búferes o ubicaciones marcadas, que puede elegir utilizando el patrón de minibuffer (por ejemplo, regexp) coincidencia.

Cuando visita un resultado de búsqueda, puede reemplazar bajo demanda, ya sea el golpe completo o solo la parte que coincide con su entrada actual de minibuffer. El reemplazo bajo demanda significa que no se le consulta sobre cada resultado de búsqueda por turno; accede a los resultados que desea directamente, en cualquier orden. Este enfoque puede ser más efectivo que query-replace si tiene un número limitado de reemplazos que realizar: omite la solicitud exhaustiva y/n.

La búsqueda es sobre la búsqueda contextos que usted define -- usted no está limitado a buscar todo el texto en el destino archivos (por ejemplo, puede omitir comentarios o tipos particulares de secciones del programa). Un ejemplo simple de un contexto de búsqueda es una línea, como en grep, pero un contexto puede ser cualquier bloque de texto que coincida con el patrón que desee. Normalmente se definen los contextos de búsqueda mediante una expresión regular, pero en su lugar se puede utilizar una función. Además de definir el suyo propio, hay comandos de búsqueda de carámbanos predefinidos para diferentes tipos de contextos: bloques de propiedades de texto o propiedades de superposición, cosas-en-punto, sucesivamente.

También puede ordenar la búsqueda se realiza en varios ordenamientos para facilitar el acceso/la navegación.

 1
Author: Drew,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-08-24 16:55:32

find-name-dired está bien, pero:

  • Todos los archivos que obtienes coinciden con la misma expresión regular.
  • find-dired es más flexible en ese sentido, pero también está hecho para usar reglas generales (incluso si pueden ser arbitrariamente complejas). Y por supuesto find tiene su propio lenguaje complejo.
  • si luego desea actuar solo sobre algunos de los archivos cuyos nombres se recopilaron en el búfer find(-name)-dired, debe marcarlos o eliminar / omitir las líneas de aquellos que no desea actuar en.

Una alternativa es utilizar Dired+ comandos que actúan sobre (a) los archivos marcados y (b) todos los archivos marcados (o todos los archivos, si ninguno está marcado) en los subdirectorios marcados ... encontrado recursivamente . Esto le da generalidad y fácil control sobre la elección del archivo. Estos comandos "aquí y abajo" están todos en la tecla prefijoM-+ en modo directo.

Por ejemplo, M-+ Q es lo mismo que Q --- query-replace, pero los archivos de destino son todos aquellos marcados en el dir actual y en cualquier subdirs marcado, abajo, abajo, abajo...

Sí, una alternativa al uso de estos comandos aquí y abajo es insertar todos los subdirs y sus subdirs, recursivamente, y luego usar un comando de nivel superior como Q. Pero a menudo puede ser conveniente no molestarse con subdirs insertados.

Y para hacer eso de todos modos necesita una forma rápida de insertar todos estos subdirs recursivamente. Aqui también, Dired+ puede ayudar. M-+ M-i inserta todos los subdirs marcados y sus propios subdirs marcados, recursivamente. Es decir, es como M-i (que inserta los subdirs marcados en Dired+), pero actúa recursivamente sobre subdirs.

(Todos los comandos" aquí y abajo " Dired+ están en el menú Múltiples > Marcado aquí y abajo .)

También puede realizar operaciones Dired en un conjunto de archivos Emacs , que es un conjunto guardado de nombres de archivos ubicados en cualquier lugar. Y si utilizas Carámbanos a continuación, puede abrir un búfer Dired solo para los archivos en un conjunto de archivos u otros tipos de listas de archivos guardados.

También puede marcar cualquier búfer Dired, incluido uno que cree usando find(-name)-dired. Esto le da una forma rápida de volver a un conjunto de este tipo (por ejemplo, un conjunto de proyectos) más tarde. Y si utilizas Marcador+ a continuación, marcar un buffer Dired registros (a) su ls conmutadores, (b) qué archivos están marcados, (c) qué subdirectorios se insertan y (d)qué (sub) directorios están ocultos. Todo eso se restaura cuando "salta" al marcador. Bookmark+ también le permite marcar un árbol entero de búferes Dired --- saltar al marcador restaura todos los búferes en el árbol.

 1
Author: Drew,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-01-26 20:52:34

Me gustaría sugerir otra gran herramienta que aún no se ha mencionado, a saber: Timón.

Es un gran reemplazo para muchas operaciones estándar de Emacs que implican finalización, búsqueda, etc. En particular, helm-find-files permite realizar el reemplazo de consultas (incluyendo regexp) dentro de varios archivos seleccionados.

Simplemente abra helm-find-files, marque los archivos relevantes con M-SPC y luego use F6 o F7 para ejecutar query replace o query replace regexp en el programa seleccionado file.

 1
Author: Andrzej Pronobis,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-05-25 03:35:09

No es Emacs, pero xxdiff viene con una herramienta llamada xx-rename que lo hará para múltiples cadenas a la vez (por ejemplo, Desde A desde a DESDE A), con solicitud interactiva, guarda copias de seguridad de todos los archivos modificados y produce un breve registro de los cambios realizados con contexto. Eso es lo que tiendo a usar cuando hago grandes / renombramientos globales.

 -2
Author: blais,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2012-08-15 16:12:54