4

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 [email protected] foo foo [email protected]

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

[email protected] [email protected]

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

2 Answers 2

6

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
:%d
:pu=lst
:1d

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 @=
3
  • Wow! Who would have thought that that would be so difficult in vim natively? I really like the grep solution though. Thanks! Commented Jul 7, 2015 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. Commented Jul 7, 2015 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
    Commented Jul 19, 2017 at 17:03
4

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

:%s/\S\+@\S\+/\r\0\r/g
:v//d
  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.

2
  • 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. Commented Jul 7, 2015 at 21:49
  • No doubt! It just seemed like a nice challenge.
    – VanLaser
    Commented Jul 7, 2015 at 21:56

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.