Replace regexp in file programatically

Hi!

If you want to replace a regexp in a file (not a buffer which is already open in Emacs, but a file), what would be the fastest and most efficient way?
(I mean, a quick equivalent to sed -i 's/old/new/g' file.txt in Bash.)

For instance, this snippet works, but is quite long for such a simple task:

(with-temp-buffer
  (find-file "file.txt")
  (goto-char (point-min))
  (while (re-search-forward "old" nil t)
    (replace-match "'new"))
  (save-buffer)
  (kill-buffer))

Do we have a simpler option?

More generally, most Emacs commands operate on buffers, not on files. When you want to operate on a file, is it canonical to open your file in a temp-buffer, do your operations, and then kill (and save) your buffer? Or is there some better way to proceed?

Thanks!

1 Like

I wish I had an answer for you but Iā€™m a lisp newbie and not an emacs user at all.
What does stackoverflow have to say about it?

1 Like

Hey fsantos,

working with temporary buffers is idiomatic and efficient.

Here is a shorter version:

(with-temp-file "file.txt"
  (insert-file-contents "file.txt")
  (replace-regexp "old" "new"))

with-temp-file creates a temp buffer and writes the content of the temp buffer to the specified file. The temp buffer gets killed automatically.

Note that the documentation warns against using replace-regexp in Emacs Lisp, but depending on your use case it could be fine:

This function is usually the wrong thing to use in a Lisp program.
What you probably want is a loop like this:
(while (re-search-forward REGEXP nil t)
(replace-match TO-STRING nil nil))
which will run faster and will not set the mark or print anything.

Also see Emacs Lisp Idioms for Text Processing in Batch Style and Emacs Lisp Text Processing: find-file vs with-temp-buffer.

You could also run the sed command with M-x shell-command (M-!).

1 Like

Thanks to both of you!
(This is very instructive @fap !)

2 Likes