I have a file that contains words that I want to save, along with other junk that I do not need. I just want to delete everything except the words that contain a certain pattern. Take email addresses for example:

foo foo foo foo foo foo@foo.com foo foo foo2@foo2.net

Run some magic and save everything from @ to the previous and next space.

foo@foo.com foo2@foo2.net

This would be useful in so many applications (especially email addresses).


Easy way - grep

The easiest technique is to use :%!grep -o {pat}. The -o/--only-matching make grep only display the matches.

:%!grep -o 'foo\w*bar'

Note: that grep's regex's are a different variant from PCRE and Vim's.

Pure Vim method with plugin - still easy

For a pure native vim solution I suggest you look at ExtractMatches or Yankitute plugins.

(Ab)Using :s for fun and profit

You want to roll your "extract matches" command with :s with a sub-replace-expression (\=) and a list.

let lst = []
:%s/pattern/\=add(lst, submatch(0))[-1]/g

The basic idea is to add each match to the list, lst, using a sub-replace-expression for the :s command. We can use some in-place array trickery to make sure the text doesn't change by always returning the last element of the array (what we just added).

This :s trick is often done in the form:

:let lst = []
:%s//\=add(lst, submatch(0))[-1]/g
:call setreg('"', join(lst, "\n"), 'l')

This will capture the current matches (uses last used pattern) into the default register. If you have Vim 7.4 then the :s can be simplified further: :%s//\=add(lst, submatch(0))/gn

More information

:h :range!
:h :s
:h sub-replace-expression
:h List
:h add()
:h submatch()
:h :d
:h :pu
:h @=
| improve this answer | |
  • Wow! Who would have thought that that would be so difficult in vim natively? I really like the grep solution though. Thanks! – Questionmark Jul 7 '15 at 19:19
  • Most of the hard work is done in the :s command. It is actually a testament to the versatility of the :s command. However it does pale in comparison to how easy this job is with grep. – Peter Rincker Jul 7 '15 at 19:24
  • This tip can do more than grep (which is line based) - delete all non-matching paragraphs in vim. E.g collect all multi-line functions in script: :%s/^function_.{-}endfunc/\\=add(lst,submatch(0))[-1]/gn, NOTE the last /n option - don't modify buffer during substition. Later paste lst as above. – mosh Jul 19 '17 at 17:03

An applied answer (pure vim stuff) that keeps email addresses only:

  1. place \r (carriage return, CR) before and after each email address (i.e. .break lines around each email found, so that each address will go on a separate line);
  2. remove all lines which are not email addresses (last search pattern is used).

Less specific usage: change \S\+@\S\+ with the desired pattern.

| improve this answer | |
  • 1
    This is a better pure Vim way. However if you want to save the matches register without destroying the buffer, then the :s with sub-replace-expression is the way to go. – Peter Rincker Jul 7 '15 at 21:49
  • No doubt! It just seemed like a nice challenge. – VanLaser Jul 7 '15 at 21:56

This is not a good job for Vim, but it's perfect for grep:

egrep -o '[^ ]*@[^ ]*' file
| improve this answer | |
  • I can just have vim call grep... :) – Questionmark Jul 7 '15 at 19:20

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.