I always try to do more things from within emacs; as said before, it's all
about return on investment for all the time spent mastering emacs. One of
the things I sometimes still used a separate terminal for, was search and replace of text in files – a quick grep
or sed
from the command line.
source code: tags
If you're searching (not replacing) symbols in source code, the most
convenient way is to use a tagfile. A while ago, I discussed
navigating through source code using tags (using
GNU Global). I still often see people
writing code in emacs, and then opening a terminal open to grep
for function
names, definition and so on… There is really no need for that; see the
linked entry.
The GNU Global tagfile system unfortunately does not have a search-and-replace
function; if you are using etags
, however, you can use M-x tags-query-replace
to replace the symbols in all files in the tagfile.
GNU Global users can of course still use the other search-and-replace mechanisms below.
other files: rgrep
For searching recursively for some strings (regexps) in a file tree, there is
the very useful M-x rgrep
. It will ask what you want to search for, some
pattern for the files to match, and the top of the directory to search.
So, for example, if you want to find all occurences of the string 'FIXME
' in
txt
-files in your ~/Documents
directory tree, you would do something like:
M-x rgrep FIXME *.txt ~/Documents
and you will get at buffer with all matches. For this, it uses the same kind
of buffer you use for compilation, and you can jump to the matching locations
just like you can jump to error locations in the compilation output - that is
why with M-x next-error
you can jump from match to match, even though of
course they are not really errors. The default shortcut for next-error
is
C-x`
, but you can of course remap that to something saner, exempli gratia:
(global-set-key (kbd "<M-prior>") 'previous-error) (global-set-key (kbd "<M-next>") 'next-error)
so you can use M-
with PgUp/PgDown
to jump through the matches (or
errors).
One final comment about rgrep
: in the above example, the FIXME
is a
regular expression (as discussed in
building regular expressions, while the *.txt
is a shell pattern (see e.g.
the findutils manual).
replacing: dired
If you want to replace text in multiple files, your best bet is to use
dired
, the emacs file manager.
dired
deserves its own entry (and probably more than one), but if we just
look at search-replace, the steps are not too hard. Suppose we want to replace
all strings FOO
with BAR
in a bunch of files in ~/myfiles/
. We open
dired
with C-x d
, enter ~/myfiles
, and a list of the files in that
directory appears.
Now, we mark the files we'd like to change by moving the cursor to them and
press m
(unmark with u
). You can also mark filenames matching some regexp
with M-x dired-mark-files-regexp
(or '* %
', obviously) or files
containing some regexp with M-x dired-mark-files-containing-regexp
(or '* g
').
After marking some files, you can use M-x dired-do-query-replace-regexp
to
interactively replace some regular expression in all of them – you have to
press 'y
' to confirm the changes. You can quit this process by pressing
'q
'.
dired
can even work recursively, as an anonymous commenter remarked
(thanks!); slightly edited:
You can type 'i
' to insert a subdirectory to the dired buffer. You can also
run M-x find-name-dired
or M-x find-dired
to generate a dired
buffer
with the results of the find
-command. Then you can mark wanted files and
perform query replace with 'Q
'. Also see: Emacs Nerdery: Search & replace across files.
As an alternative, you can use an external package like FindR for recursive search & replace. I haven't used that one myself though, as I haven't had the need yet.
7 comments:
"Dired does not work recursively - it's one directory at a time."
Hmm, not entirely true in practice. You can type "i" to insert subdirectory to the dired buffer. You can also run "M-x find-name-dired" or "M-x find-dired" to generate a dired buffer with find command. Then you can mark wanted files and perform query replace (Q).
Check blog post Search & replace across files.
@Anonymous: thanks! updated.
In dired you can also do...
M-x wdired-change-to-wdired-mode
(long to type, but you can bind some key to it)
And then it will make the dired buffer editable. You can then search-replace filenames or do any editing the usual way you would edit lines in a text buffer, and then apply changes with C-x C-s.
ah, sorry, my bad. wdired is for renaming files, your nice tip is for replacing expressions in multiple files.
Nice tips
One thing I don't like about rgrep and friends is that emacs modifies your search regexp before passing it to grep. I disable this by changing the appropriate grep-expand-keywords.
;; modify grep-expand-keywords so that regexp is passed unmodified
(let ((assocR (assoc "<R>" grep-expand-keywords)))
;; was originally ("<R>" shell-quote-argument (or regexp ""))
(when assocR
(setcdr assocR (list 'concat "'" 'regexp "'"))))
Perhaps it's because I use extended-regexp mode in grep instead:
(setq grep-find-template
;; was originally "find . <X> -type f <F> -print0 | xargs -0 -e grep <C> -nH -e <R>"
"find . <X> -type f \\( ! -name '*~' \\) -a -type f <F> -print0 | xargs -0 -e grep <C> --extended-regexp -nH -e <R>")
You can also open a dired including all subfolders recursively by passing a numerical argument to dired:
M-1 M-x dired
(thats M-'one', not M-'ell').
You then get prompted for the flags you would like to pass to dired (nothing more than the arguments you would pass to 'ls' under Unix). Hence for a recursive list, add -R to the list of options.
With Icicles you can easily search (and replace on demand) through multiple files. You can choose the files interactively (using multiple-pattern matching if you want) or take them from a file or fileset.
With Dired+ and Icicles you can search (and replace on demand) through the marked files, including those in marked subdirectories, recursively.
http://www.emacswiki.org/emacs/Icicles_-_Dired_Enhancements#toc2
Post a Comment