5

I'd like to remove everything apart of my pattern match.

For example I've the following line:

window.page4_callback(["here \"is\" some text"]);

and I'd like to leave the text only within the double quotes, so the expected output is:

here \"is\" some text 

Matching pattern is /".*" and its removal can be performed by using substitute:

:s/".*"//g

however how do you do opposite?

I've tried to use vglobal (:v/pattern/d), but it would normally remove the whole line, but in my case it's saying:

Pattern found in every line: ".*"

I've found similar issue here, but I'm even more confused.

8

There are probably a bunch of ways to do this; here's my attempt:

:%s/.*"\(.\{-}\)".*/\1/

The key part is "\(.\{-}\)"; this non-greedily matches everything in between quotes. This will match:

window.page4_callback(["here is some text"]);

The parentheses are there to start a subgroup, so we can reference it with \1 on the replacing side. We surround this with .* on both ends to match the rest of the line.

So this doesn't really "delete a non-matching pattern", as the title says. Rather, it deletes the entire line, and replaces it with a specific subgroup we matched earlier. The effect is the same, and for me, this is an easier way to think about it.

Conclusion: capturing subgroups is good. Subgroups are your friends.


Edit

To also work after your edited post :-)

:%s/^.\{-}"\(.*\)".\{-}$/\1/

This is a variant of the above, but notice we use .\{-} before and after the " now, to match as little as possible; plus, we anchor it to the start and end of the line with ^ and $.

You can then replace the \" with a second :substitute command:

:%s/\\"/"/

This still won't work with something like:

window.function("arg1", "arg2");

This will leave you with:

arg1", "arg2

But I'm not sure if that's a requirement :-)

  • You don't need .\{-} here, .* would match the same thing. In both cases the last fragment that's surrounded by double quotes is retained. – Gilles May 8 '15 at 21:01
3
yi[       " yank what's between the brackets
S         " delete line and start insert
<C-r>0    " insert last yanked text
3

The easiest way is to use grep --only-matching.

:%!grep --only-matching --extended-regexp '".*"'

However if you want a completely Vim solution then you can use :s with an expression replacement:

:let @a=""
:%s//\=setreg('A', submatch(0), 'l')/g
:%d _
:pu a
:0d _

Or all on a single line:

:let @a=""|%s/".*"/\=setreg('A', submatch(0), 'l')/g|%d _|pu a|0d _

In brief, this uses a substitution to append each match into register "a" linewise then replace the entire buffer with the contents of register "a"

See my answer at "Remove everything except regex match in Vim" for a fuller explanation.

Concerns:

  • This will trash one of your registers. This example trashed @a
  • Uses the last search pattern. Although you can modify the substitute command with whatever pattern you want: %s/<pattern>/\=setreg('A', submatch(0), 'l')/g
  • Thanks, I actually used grep -o '".*"' for sake of simplicity. – kenorb May 9 '15 at 9:27
2

You could build up a list of the matches, delete everything, and then paste the match list:

let old_c = @c

let @c=""
exec a:firstline .','. a:lastline .'sub//\=setreg("C", submatch(0), "l")/g'
exec a:firstline .','. a:lastline .'delete _'
put! c

let @c = old_c

vim-searchsavvy provides a :ClearAllButMatches command that uses the above code. You do your search, call the command, and only text that matched the command remain. Multiple matches on one line will be put on separate lines.

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.