3

Suppose I have the following line of text in vim:

hello, it will be sunny today

and I want to change it to:

goodbye, it will be sunny tomorrow

I could put my cursor on the line and type :s/hello/goodbye, hit enter, and then type :s/today/tomorrow. But is there any way to make both changes in the same substitute command? For example, by doing something like :s/hello/goodbye/today/tomorrow? (obviously this specific syntax doesn't work, but it's just an example to illustrate what I want.)

4
  • 4
    Some aren't applicable here, but there's various solutions in this duplicate question: Simultaneous find and replace
    – Rich
    Oct 25 at 9:48
  • Looks like most of the answers there are based on sub-replace expressions which I intentionally glossed over but if one's interested in that route that's a good resource.
    – B Layer
    Oct 25 at 13:06
  • 1
    @BLayer There's also 5 answers there that don't use sub-replace expressions, including two of the suggestions from your own (excellent) answer below.
    – Rich
    Oct 27 at 16:09
  • @Rich You're too kind. ;) Yep, I didn't mean to suggest it was all sub-replace...just that it had a lot of examples of that. I guess we should dupe this sucker, huh? (Just voted.)
    – B Layer
    Oct 27 at 16:13
4

No, there's no systematic, built-in mechanism to do a two string swap as you describe. It's really not needed since you can do it using standard regex/replacement semantics.

For instance:

:s/^hello\(.*\)today$/goodbye\1tomorrow

Obviously, this only works if the two strings are at the start and end of the string. In other cases, you'll need to modify the regex but the same idea of using capture-group(s) and \# in the replacement will usually be applicable.

Now if you want to replace multiple occurrences of the two strings you're really better off running two substitutions. You can put them on the same line...

:s/today/tomorrow/g | :s/hello/goodbye/g

This actually could be done in a single substitution using a sub-replace expression but a general solution would be relatively complex and won't buy you much, really, compared to the simple pair of substitutions, above. (Note that a lot of people find sub-replace expressions hard to understand.) That being said, take a look at the link in the comment from @Rich for examples and I have a modestly well-received :) answer with a relatively deep dive into them, too.

And, of course, you could implement this functionality using a custom function (vimscript). But that wasn't your question. :)

Update: If you're willing to install a plugin you can get precisely the functionality you described with Tim Pope's vim-abolish. For your example the solution would look like this:

:S/{hello,today}/{goodbye,tomorrow}/gw

Thanks to @PeterRincker for this suggestion. He also recommends a vimcast episode to learn more about the plugin.

4
  • 2
    vim-abolish's :Subvert can make this easy. e.g. :S/{hello,today}/{goodbye,tomorrow}/gw Oct 26 at 14:27
  • @PeterRincker Cool. Do you think that's worth an answer? Otherwise, I could mention it in mine.
    – B Layer
    Oct 27 at 0:10
  • 1
    please feel free to add it as an option to your post. There is also a nice vimcast episode which might be helpful Oct 27 at 0:13
  • @PeterRincker Will do so as soon as I get a chance. Thanks. (I actually have abolish but have never gotten around to learning it.)
    – B Layer
    Oct 27 at 0:16

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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