Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Using Emacs to recursively find and replace in text files not already open

Tags:

emacs

editor

  1. M-x find-name-dired: you will be prompted for a root directory and a filename pattern.
  2. Press t to "toggle mark" for all files found.
  3. Press Q for "Query-Replace in Files...": you will be prompted for query/substitution regexps.
  4. Proceed as with query-replace-regexp: SPACE to replace and move to next match, n to skip a match, etc.
  5. Press C-x s to save buffers. (You can then press y for yes, n for no, or ! for yes for all)

  • M-x find-name-dired RET
    • it may take some time for all the files to appear in the list, scroll to bottom (M->) until "find finished" appears to make sure they all have loaded
  • Press t to "toggle mark" for all files found
  • Press Q for "Query-Replace in Files...": you will be prompted for query/substitution regexps.
  • Proceed as with query-replace-regexp: SPACE or y to replace and move to next match, n to skip a match, etc.
    • Type ! to replace all occurrences in current file without asking, N to skip all possible replacement for rest of the current file. (N is emacs 23+ only)
    • To do the replacement on all files without further asking, type Y.
  • Call “ibuffer” (C-x C-b if bound to ibuffer, or M-x ibuffer RET) to list all opened files.
  • Type * u to mark all unsaved files, type S to save all marked files
  • * * RET to unmark all marks, or type D to close all marked files

This answer is combined from this answer, from this site, and from my own notes. Using Emacs 23+.


Projectile is really nice: C-c p r runs the command projectile-replace


The answers provided are great, however I thought I'd add a slightly different approach.

It's a more interactive method, and requires wgrep, rgrep and iedit. Both iedit and wgrep must be installed via MELPA or Marmalade (using M-x package-list-packages)

First run M-x rgrep to find the string you're looking for.

You'll be able to specify file types/pattern and the folder to recurse.

Next you'll need to run wgrep start it with C-s C-p.

Wgrep will let you edit the rgrep results, so set a region on the string to match and start iedit-mode with C-; (depending on your terminal you may need to re-bind this)

All occurrences will be editable at once. C-x C-s to commit wgrep. Then C-x s ! to save the changed files.

The main benefit of this method is that you can use iedit-mode to toggle off certain matches M-;. You can also use the results in rgrep to jump into the files, for example if you have an unexpected match.

I find it very useful for doing source edits and renaming symbols (variables, function names etc.) across a project.

If you don't already know/use iedit mode it's a very handy tool, I strongly recommend you give it a look.


I generally use other tools to perform this task, and it seems like many of the approaches mentioned at EmacsWiki's Find and Replace Across Files entry shell out, but the Findr Package looks very promising.

Stealing part of the source file:

(defun findr-query-replace (from to name dir)
  "Do `query-replace-regexp' of FROM with TO, on each file found by findr.

Source of information: 1

For emacs pro users:

  1. Call dired to list files in dir, or call find-dired if you need all subdirectories.
  2. Mark the files you want. You can mark by regex by typing 【% m】.
  3. Type Q to call dired-do-query-replace-regexp.
  4. Type your find regex and replace string. 〔☛ common elisp regex pattern〕
  5. For each occurrence, type y to replace, n to skip. Type 【Ctrl+g】 to abort the whole operation.
  6. Type ! to replace all occurrences in current file without asking, N to skip all possible replacement for rest of the current file. (N is emacs 23 only)
  7. To do the replacement on all files without further asking, type Y. (Emacs 23 only)
  8. Call ibuffer to list all opened files. Type 【* u】 to mark all unsaved files, type S to save all marked files, type D to close them all.

Step-by-Step Guide for Emacs Beginners

Select Target Files

Start emacs by typing “emacs” in the command line interface prompt. (Or, double click the Emacs icon if you are in a Graphics User Interface environment)

Selecting Files in a Directory

First you need to select the files you want to do the replace. Use the graphical menu 〖File ▸ Open Directory〗. Emacs will ask you for a directory path. Type the directory path, then press Enter.

Now, you will be shown the list of files, and now you need to mark the files you want the regex find/replace to work on. You mark a file by moving the cursor to the file you want, then press m. Unmark it by pressing u. (To list subdirectories, move your cursor to the directory and press i. The sub-directory's content will be listed at the bottom.) To mark all files by a regex, type 【% m】, then type your regex pattern. For example, if you want to mark all HTML files, then type 【% m】 then .html$. (You can find a list of the mark commands in the graphical menu “Mark” (this menu appears when you are in the dired mode).)

Selecting Files in a Directory and All Its Sub-Directories

If you want to do find/replace on files inside a directory, including hundreds of subdirectories, here's a method to select all these files.

Call find-dired. (you call a command by pressing 【Alt+x】) Then, type a directory name, ⁖ /Users/mary/myfiles

Note: if you are using emacs on a unix non-graphical text terminal, and if 【Alt+x】 does not work, the equivalent key stroke is 【Esc x】.

Emacs will ask you with the prompt “Run find (with args): ”. If you need to do the replacement on all HTML files, then type -name "*html". If you don't care about what kind of file but simply all files under that dir, then give “-type f”.

Now, mark the files as described above.

Interactive Find/Replace

Now, you are ready to do the interactive find replace. For simplicity, let's say you just want to replace the word “quick” by “super”. Now, call dired-do-query-replace-regexp. It will prompt you for the regex string and the replacement string. Type “quick”, enter, then “super”.

Now, emacs will use your pattern and check the files, and stop and show you whenever a match occurred. When this happens, emacs will prompt you, and you have a choice of making the change or skip the change. To make the change, type y. To skip, type n. If you simply want emacs to go ahead and make all such changes to the current file, type !.

If you want to cancel the whole operation without saving any changes you've made, type 【Ctrl+g】, then exit emacs using the menu 〖File ▸ Exit Emacs〗.

Saving the Changed Files

Now, after you went through the above ordeal, there is one more step you need to do, and that is saving the changed files.

If you are using emacs version 22 or later, then call ibuffer to go into a buffer listing mode, then type 【* u】 to mark all unsaved files, then type S to save them all. (that's shift-s)

If you are using a emacs version 21, then you can do this: call list-buffers, then move the cursor to the file you want to save and type s. It will mark the file for later save action. Type u to unmark. Once you are done, type x to execute the saving of all files marked for save. (in emacs, opened file is called “buffer”. Disregard other things there.)

Alternative to the above options, you can also call save-some-buffers 【Ctrl+x s】. Then emacs will display each unsaved file and ask if you want it saved.

Note: emacs's regex is not the same as Perl or Python's, but similar. For a summary and common patterns, see: Emacs Regex.


Using dired to recurse down a deep directory tree is going to be a bit slow for this task. You might consider using tags-query-replace. This does mean shelling out to create a tags table, but that is often useful anyway, and it's quick.