Usando Emacs para buscar y reemplazar recursivamente en archivos de texto que aún no están abiertos

212

Como seguimiento a esta pregunta , está tratando de descubrir cómo hacer algo como esto, lo que debería ser fácil, que especialmente me impide acostumbrarme más a usar Emacs y, en cambio, iniciar el editor con el que ya estoy familiarizado. Utilizo el ejemplo aquí con bastante frecuencia en la edición de múltiples archivos.

En Ultraedit, haría Alt + s y 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, Casos coincidentes, Solo palabras completas, Lista Archivos modificados y subdirectorios de búsqueda. Por lo general, primero usaré el mouse para hacer clic y arrastrar, seleccionar el texto que quiero reemplazar.

Usando solo Emacs en sí (en Windows XP), sin llamar a ninguna utilidad externa, cómo reemplazar todo foo \ nbar con bar \ nbaz *.cy *.harchivos en alguna carpeta y todas las carpetas debajo de ella. Quizás Emacs no sea la mejor herramienta para hacer esto, pero ¿cómo se puede hacer fácilmente con un comando mínimo?

Rob Kam
fuente

Respuestas:

370
  1. M-x find-name-dired: se le solicitará un directorio raíz y un patrón de nombre de archivo.
  2. Presione tpara "alternar marca" para todos los archivos encontrados.
  3. Presione Q"Query-Replace in Files ...": se le solicitarán expresiones regulares de consulta / sustitución.
  4. Proceda como con query-replace-regexp: SPACEpara reemplazar y pasar al siguiente partido, nomitir un partido, etc.
  5. Presione C-x spara guardar buffers. (Puede continuación, pulse y, no !para salvar a todos a la vez)
Chris Conway
fuente
10
No, es recursivo. La versión no recursiva sería% m en modo directo.
Chris Conway
55
Quizás porque estás llamando a C: \ WINDOWS \ system32 \ find.exe, no a GNU find.
Jazz
66
Steen, Chris: Probablemente no sea exactamente lo que quieres, pero puedes usar 'Cx s' (guardar algunos buffers), te solicitará cada archivo modificado, por lo que solo debes presionar 'y' varias veces.
danielpoe 03 de
48
C-x s !para guardar todos los archivos a la vez.
cYrus
10
M-,para continuar buscando y reemplazando (por ejemplo, después del mensaje "revertir archivo" si el archivo cambió en el disco).
tfischbach
37
  • M-x find-name-dired RET
    • Puede tomar algún tiempo para que todos los archivos aparezcan en la lista, desplácese hacia abajo ( M->) hasta que find finishedaparezca " " para asegurarse de que todos se hayan cargado.
  • Presione tpara "alternar marca" para todos los archivos encontrados
  • Presione Q"Query-Replace in Files ...": se le solicitarán expresiones regulares de consulta / sustitución.
  • Proceda como con query-replace-regexp: SPACE o ypara reemplazar y pasar a la siguiente coincidencia, n para omitir una coincidencia, etc.
    • Tipo ! para reemplazar todas las ocurrencias en el archivo actual sin preguntar, Npara omitir todos los reemplazos posibles para el resto del archivo actual. (solo Nes emacs 23+)
    • Para hacer el reemplazo de todos los archivos sin más preguntas, escriba Y.
  • Llame a "ibuffer" ( C-x C-bsi está vinculado a ibuffer, o M-x ibuffer RET) para enumerar todos los archivos abiertos.
  • Escriba * upara marcar todos los archivos no guardados, escriba Spara guardar todos los archivos marcados
  • * * RETpara desmarcar todas las marcas o escribir Dpara cerrar todos los archivos marcados

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

Frank Henard
fuente
3
ibufferno está obligado C-x C-bpor defecto.
PHILS
2
Así personalmente me recomiendan que las personas no se unen C-x C-ba ibuffer, porque es mucho mejor que la unión por defecto. Simplemente agréguelo (global-set-key (kbd "C-x C-b") 'ibuffer)a su archivo .emacs. De lo contrario, use M-x ibuffer RETen las instrucciones anteriores.
phils
Okay. Creo que tengo el kit de inicio de emacs, por eso el mío está obligado
Frank Henard
1
El archivo bla-bla-blase visita como de solo lectura y deja de buscar. ¿Cómo evitar / ignorar eso y seguir reemplazando? Gracias.
Felipe
3
¿Por qué ibuffercuando puedes C-x s( save-some-buffers) y escribes !? No quiero despotricar, solo curiosidad. PS plus para describir !, Ny Y.
d12frosted
17

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, rgrepy iedit. Ambos iedity wgrepdeben instalarse a través de MELPA o Marmalade (usando M-x package-list-packages)

Primero corre M-x rgreppara encontrar la cadena que estás buscando.

Podrá especificar los tipos de archivo / patrón y la carpeta para repetir.

A continuación, deberá ejecutar wgrepcomenzar con C-s C-p.

Wgrep le permitirá editar los rgrepresultados, por lo que establecer una región de la cadena para que coincida y empezar iedit-modecon C-;(dependiendo de su terminal puede que tenga que volver a enlazar este)

Todos los sucesos serán editables a la vez. C-x C-sa cometer wgrep. Luego C-x s !para guardar los archivos modificados.

El principal beneficio de este método es que puede usar iedit-modepara desactivar ciertas coincidencias M-;. También puede usar los resultados rgreppara saltar a los archivos, por ejemplo, si tiene una coincidencia inesperada.

Me resulta muy útil para realizar ediciones de origen y renombrar símbolos (variables, nombres de funciones, etc.) en un proyecto.

Si aún no conoce / utiliza el modo iedit, es una herramienta muy útil, le recomiendo que le eche un vistazo.

ocodo
fuente
Esta técnica es bastante poderosa incluso sin usar iedit. ¡Convierte saber cómo grep (fácil con lgrep / rgrep) en saber cómo buscar-reemplazar en masa!
NeilenMarais
Combinar rgrep / wgrep / macros es increíble.
ocodo
2
La combinación de teclas para comenzar wgrepes por C-c C-pcierto.
techniao
15

El proyectil es realmente agradable: Cc pr ejecuta el comando projectile-replace

eflanigan00
fuente
13

Generalmente uso otras herramientas para realizar esta tarea, y parece que muchos de los enfoques mencionados en la entrada de EmacsWiki Buscar y reemplazar entre archivos se abren , pero el Paquete Findr parece muy prometedor.

Robar parte del archivo fuente :

(defun findr-query-replace (from to name dir)
  "Do `query-replace-regexp' of FROM with TO, on each file found by findr.
Blair Conrad
fuente
¿Cómo trato de encontrar la cadena "Coleccionable" en todos los archivos con extensión java y hxx usando findr.el?
swdev
@swdev: Mx findr-query-replace RET Collectible RET <el reemplazo> RET. * \. java RET <el directorio para comenzar a buscar en> RET
ShreevatsaR
Me gusta este enfoque (evita todo el cambio involucrado en el enfoque directo). Utilizo algunas funciones de conveniencia para combinar esto con proyectil (project-root-discover), y reemplazar la cosa en el punto: gist.github.com/anonymous/055a9d62f9fc824153599724b8f44d57
Att Righ
6

Fuente de información: 1

Para 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 desee. Puede marcar con expresiones regulares escribiendo 【% m】.
  3. Escriba Q para llamar a dired-do-query-replace-regexp.
  4. Escriba su regex de búsqueda y reemplace la cadena. 〔☛ patrón de expresión regular de elisp〕
  5. Para cada aparición, escriba y para reemplazar, n para omitir. Escribe 【Ctrl + g】 para cancelar toda la operación.
  6. Tipo ! para reemplazar todas las ocurrencias en el archivo actual sin preguntar, N para omitir todos los reemplazos posibles para el resto del archivo actual. (N es solo emacs 23)
  7. Para hacer el reemplazo en todos los archivos sin más preguntas, escriba Y. (solo Emacs 23)
  8. Llame a ibuffer para enumerar todos los archivos abiertos. Escriba u * 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 principiantes de Emacs

Seleccionar archivos de destino

Inicie emacs escribiendo "emacs" en el indicador 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 gráfica de usuario)

Seleccionar archivos en un directorio

Primero debe seleccionar los archivos que desea reemplazar. Use el menú gráfico 〖Archivo ▸ Abrir directorio〗. Emacs le pedirá una ruta de directorio. Escriba la ruta del directorio, luego presione Entrar.

Ahora, se le mostrará la lista de archivos, y ahora debe marcar los archivos en los que desea que funcione la búsqueda / reemplazo de expresiones regulares. Marque un archivo moviendo el cursor al archivo que desee y luego presione m. Desmarque presionando u. (Para enumerar subdirectorios, mueva el cursor al directorio y presione i. El contenido del subdirectorio aparecerá en la parte inferior.) Para marcar todos los archivos por una expresión regular, escriba 【% m】, luego escriba su patrón de expresión regular. Por ejemplo, si desea marcar todos los archivos HTML, escriba 【% m】 y luego .html $. (Puede encontrar una lista de los comandos de marca en el menú gráfico "Marcar" (este menú aparece cuando está en el modo directo).)

Selección de archivos en un directorio y todos sus subdirectorios

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

Llame a find-dired. (puede llamar a un comando presionando 【Alt + x】) Luego, escriba un nombre de directorio, ⁖ / Users / mary / myfiles

Nota: si está utilizando emacs en un terminal de texto no gráfico de Unix, y si 【Alt + x】 no funciona, la pulsación de tecla equivalente es 【Esc x】.

Emacs le preguntará con el mensaje "Ejecutar find (con args):". Si necesita reemplazar los archivos HTML, escriba -name "* html". Si no le importa qué tipo de archivo, sino simplemente todos los archivos que se encuentran en ese directorio, escriba "-tipo f".

Ahora, marque los archivos como se describe arriba.

Buscar / reemplazar interactivo

Ahora, está listo para hacer el reemplazo interactivo de búsqueda. Para simplificar, supongamos que solo desea reemplazar la palabra "rápido" por "super". Ahora, llame a dired-do-query-replace-regexp. Le pedirá la cadena de expresiones regulares y la cadena de reemplazo. Escriba "rápido", ingrese, luego "super".

Ahora, emacs usará su patrón y verificará los archivos, y se detendrá y le mostrará cada vez que ocurra una coincidencia. Cuando esto sucede, emacs le avisará, y usted tiene la opción de hacer el cambio o saltear el cambio. Para hacer el cambio, escriba y. Para omitir, escriba n. Si simplemente desea que emacs continúe y realice todos los cambios en el archivo actual, escriba!.

Si desea cancelar toda la operación sin guardar los cambios que haya realizado, escriba 【Ctrl + g】, luego salga de emacs usando el menú 〖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, llame a ibuffer para ingresar al modo de listado de búfer, luego escriba 【* u】 para marcar todos los archivos no guardados, luego escriba S para guardarlos a todos. (eso es shift-s)

Si está utilizando una versión 21 de emacs, puede hacer esto: llamar a los buffers de lista, luego mover el cursor al archivo que desea guardar y escribir s. Marcará el archivo para guardarlo más tarde. Escriba u para desmarcar. Una vez que haya terminado, escriba x para ejecutar el almacenamiento de todos los archivos marcados para guardar. (en emacs, el archivo abierto se llama "búfer". No tenga en cuenta otras cosas allí).

Alternativa a las opciones anteriores, también puede llamar a save-some-buffers 【Ctrl + xs】. Luego, emacs mostrará cada archivo no guardado y le preguntará si desea guardarlo.

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

Prashant Sharma
fuente
2
Esta respuesta debería hacerlo más amigable para los novatos que la respuesta más votada, espero. Por cierto, gracias por motivarme a poner todo en lugar del enlace.
Prashant Sharma
La respuesta comienza con la fuente: como la primera línea. La justificación para copiar y pegar es: a veces el enlace externo a los blogs queda desactualizado y luego la respuesta se volverá inútil. Y así, como lo sugiere el meta meta. Decidí copiar todo el asunto.
Prashant Sharma
5

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

Alex Coventry
fuente
Acabo de probar en un directorio con más de 14,000 archivos. Tardó unos segundos en aparecer en Emacs. Así que definitivamente no es lento (más).
Berend de Boer
3

Para los buffers 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)))))
yPhil
fuente
3

M-X Dired, yt para marcar todos los archivos y Qpara consultar el texto de reemplazo en todos ellos. Puede expandir un subdirectorio utilizando el icomando antes de la consulta de reemplazo. La información clave que estoy agregando es que si le das un prefijo (control-u) al comando i, te pedirá arg, y el argumento -R expandirá recursivamente todo subdirs en el diredbúfer. Por lo tanto, ahora puede realizar búsquedas de búsqueda en todos los archivos de un directorio completo.

Zack Murray
fuente
2

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

Puede buscar cualquier número de archivos, almacenamientos intermedios o ubicaciones marcadas, que puede elegir utilizando la coincidencia de patrón de minibúfer (por ejemplo, expresiones regulares).

Cuando visita un hit de búsqueda, puede reemplazar a pedido el hit completo o solo una parte que coincida con su entrada de minibúfer actual. El reemplazo bajo demanda significa que no se le consulta sobre cada búsqueda por turno; accede a los éxitos que desea directamente, en cualquier orden. Este enfoque puede ser más efectivo que el reemplazo de consultas si tiene que realizar un número limitado de reemplazos: omite las instrucciones exhaustivas y/n.

La búsqueda se realiza en contextos de búsqueda que usted define: no está limitado a buscar todo el texto en los archivos de destino (por ejemplo, puede omitir comentarios o tipos particulares de secciones de 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 un patrón que desee. Por lo general, define los contextos de búsqueda utilizando una expresión regular, pero en su lugar puede utilizar una función. Además de definir el suyo propio, hay comandos de búsqueda de Icicles predefinidos para diferentes tipos de contextos: bloques de propiedades de texto o propiedades de superposición, cosas de punto en punto, etc.

También puede ordenar los resultados de búsqueda en varios órdenes de clasificación para facilitar el acceso / navegación.

Dibujó
fuente
2

find-name-dired está bien, pero:

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

Una alternativa es usar los comandos Dired + 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 ... encontrados de forma recursiva . Esto le brinda generalidad y un control sencillo sobre la elección del archivo. Estos comandos "aquí y abajo" están todos en la tecla de prefijo M-+en modo Dired.

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

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

Y para hacerlo, de todos modos necesita una forma rápida de insertar todos esos subdireccionamientos de forma recursiva . Aquí también, Dired + puede ayudar. M-+ M-iinserta todos los subdirs marcados y sus propios subdirs marcados, de forma recursiva. Es decir, es como M-i(que inserta los subdirs marcados en Dired + ), pero actúa de forma recursiva en subdirs.

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

También puede realizar operaciones Dired en una Emacs conjunto de archivos , que es un conjunto guardado de nombres de archivos que se encuentran en cualquier lugar. Y si usa Icicles , 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 brinda una forma rápida de volver a ese conjunto (por ejemplo, un conjunto de proyectos) más adelante. Y si usa Bookmark +, luego marcar un búfer Dired registra (a) sus lsinterruptores, (b) qué archivos están marcados, (c) qué subdirectorios se insertan y (d) qué (sub) directorios están ocultos. Todo eso se restaura cuando "saltas" al marcador. Bookmark + también le permite marcar un árbol completo de buffers Dired --- saltar al marcador restaura todos los buffers en el árbol.

Dibujó
fuente
Quien: ¿Le importa explicar por qué rechazó esta respuesta?
Dibujó
1

Me gustaría sugerir una gran herramienta más que aún no se ha mencionado, a saber, Helm .

Es un gran reemplazo para muchas operaciones estándar de Emacs que implican la finalización, búsqueda, etc. En particular, helm-find-filespermite realizar el reemplazo de consultas (incluida la expresión regular) dentro de múltiples archivos seleccionados.

Simplemente abra helm-find-files, marque los archivos relevantes con M-SPCy luego use F6o F7para ejecutar la consulta reemplazar o la consulta reemplazar regexp en los archivos seleccionados.

Andrzej Pronobis
fuente
Puede helm-find-filesmarcar por regexp en lugar de M-SPC?
Nordlöw
-2

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

blais
fuente