13

One of the things I miss from vim is being able to type in a substitution command that will work over multiple lines, for example:

:/begin/,/end/s/foo/bar/g

The above command substitutes "foo" for "bar" starting with the first line containing "begin" and ending with the next line after that containing "end".

Is there a way to do something similar in emacs?

4 Answers 4

7

evil provides a stripped-down version of ex, so it's probably best to presume that it's not an exact drop-in. However, the example you provided works out of the box, provided that point is prior to the first line (ie, the begin line in your example).

1
  • In vim the search would also start at point, so that sounds like what I was looking for. Thanks! Commented Oct 21, 2014 at 13:46
10

Here is one way of doing it that uses built-in functionality only:

  1. With point in the line that contains first occurrence of begin, press C-SPC.

  2. Move to next occurrence of end:

    C-s end RET

  3. Replace foo with bar:

    M-% foo RET bar RET !

This makes use of the fact that query-replace will work on the active region instead of the whole buffer if there is one.


Of course, you can also define a custom command:

(defun replace-from-to (beg end str repl)
  (interactive "sBegin: \nsEnd: \nsString: \nsReplacement: ")
  (save-excursion
    (goto-char (point-min))
    (let ((start-pos (search-forward beg))
          (end-pos (search-forward end)))
      (replace-string str repl nil start-pos end-pos))))

This command will always search from the beginning of the buffer, so point can be after begin/foo/end when you invoke it.

Set up a key binding for it via:

(global-set-key (kbd "C-c r") 'replace-from-to)
5

In general, this is something that you would use narrow-to-region for.

You move the cursor (for example, by searching) to the beginning of the region and press C-SPC, then move to the end of the region and type M-x narrow-to-region. Now you can issue any search and replace commands that you want and they will only apply to narrowed part of the buffer. Once you're done, type M-x widen to restore the buffer content.

2
  • 4
    If begin/end are delimiters in a programming language, you can likely use C-M-Space to run mark-sexp to select the region in one command.
    – dgtized
    Commented Oct 21, 2014 at 6:56
  • 1
    Default bindings: C-x n n (narrow-to-region) and C-x n w (widen).
    – itsjeyd
    Commented Oct 21, 2014 at 7:26
0

Nine years late, but I was looking for something like this, too. I wanted to be able to run sed-style regex commands in emacs the way I do from the ex command line in vim. evil-mode's ex-like functionality sort of hits the spot, as mentioned above, but I don't really want to use evil. Running sed commands on selected regions or buffers is all I really need. So I tossed together a package this morning that does this. You can find it at https://github.com/oblivia-simplex/emacs-sedition.git, or install it with straight.el (or any other use-package extension that lets you pull from git repos). Once I've had a chance to kick the tires a little, I'll publish it on MELPA.

Here's the function that does all the work:

    (defun sedition ()
      "Pipe the contents of the region to the unix tool sed.
When called interactively, `REGION-BEGINNING' and `REGION-END'
refer to the beginning and end points of the active region.
If no region is active, they are set to the beginning and end of
the current buffer."
      (interactive)
      (let* ((in-region (use-region-p))
         (start (if in-region (region-beginning) (point-min)))
         (end (if in-region (region-end) (point-max)))
         (prompt (if in-region
                 (format "sed (region %d-%d): " start end)
                   "sed buffer: "))
             (command (read-string prompt nil 'sed--command-history))
         ;; generate-new-buffer will create a unique name with this prefix
         ;; the initial space hides it from list-buffers and buffer-menu
         ;; commands
         (tmpbuffer (generate-new-buffer " *sed*"))
         (exitcode (call-process-region start
                        end
                        "sed"
                        nil ;; don't delete anything yet
                        tmpbuffer
                        t
                        command)))
        ;; We only want to perform any action on the region/buffer
        ;; if the command succeeded, so check the exit code here.
        (cond ((zerop exitcode)
           (delete-region start end)
           (goto-char start)
           (insert-buffer-substring tmpbuffer))
          ((= exitcode 127)
           (message "sed: command not found"))
          ((= exitcode 1)
           (message "sed: error in command '%s'" command))
          ((stringp exitcode)
           (message "sed: %s" exitcode))
          (t (message "sed: exited with code %d" exitcode))
          )
        (kill-buffer tmpbuffer)))

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.