19

I need to search for lines which contain some word but do not contain a second word.

For example, find lines that contain the word "This" but not the word "red"

so that for a file containing these lines

This is a blue coat
This is a red  coat
This is a purple coat
That is a coat

The search would locate the first and third lines only.

Thanks for any advice!

muru
  • 24,838
  • 8
  • 82
  • 143
Leo Simon
  • 715
  • 1
  • 5
  • 11

3 Answers3

30

You can use this command to print lines containing This and not red:

:g/\(.*This\)\&\(.*red\)\@!
  • \& separates the branches like a logical AND because we want the lines that satisfy the two conditions (branches),
  • \(.*This\) is the first branch, it could simply be This but it is more generic that way,
  • \(.*red\)\@! is the second branch, \@! means "zero width match if the preceding atom doesn't match", like a logical NOT.

In other words:

\(.*This\)  \&  \(.*red\)\@!
°°°°°°°°°°  °°  °°°°°°°°°°°°
'This'      AND NOT 'red'
romainl
  • 40,486
  • 5
  • 85
  • 117
  • 2
    Much thanks, this is very much like a query through formatted text. I was able to use this to find functions missing docstrings. –  Jul 23 '18 at 17:14
9

And there is also another way with :global command, as follows:

:g/This\C/v/red/p

and the first :g command filter the lines contains This, and then passed into the second :v command to show lines that don't contain red, and at last, p (short for print) is applied to those lines with matches.

This is the charm of vim.

Leo Simon
  • 715
  • 1
  • 5
  • 11
Troy Daniel
  • 106
  • 1
  • 2
  • Thanks @TroyDaniel, could you explain what the \C does please? – Leo Simon May 17 '20 at 16:07
  • In vim, the \C in regex means case-sensitive, and \c (with lower c) means case-insensitive. For more details, you can :h \C. – Troy Daniel May 17 '20 at 17:35
  • Thanks @TroyDaniel, I've been wanting to know how to do case-insensitive searches for decades! But your suggestion includes red, rather than excluding it, thus returning just the second line. Could you modify your answer please to exclude red? I tried playing around with the @ syntax above but couldn't get it to work in your setting – Leo Simon May 17 '20 at 18:04
  • As long as you know :g command, you shold know :g! or :v command. If not, please do :h :v. – Troy Daniel May 18 '20 at 13:47
  • Thus, it's simple to modify the above command as: :g/This\C/v/red/p, which acts as you want. – Troy Daniel May 18 '20 at 13:48
  • 4
    Note: this needs at least Vim 8.0.630. Using :g recursively was disallowed in older versions – Christian Brabandt May 19 '20 at 08:14
  • @LeoSimon On the off chance you're not already aware of these, there's also :help 'ignorecase' and :help 'smartcase', if you want to use case-insensitive searches on a more permanent basis. – Rich May 19 '20 at 08:34
3

Take a look at this post.

Combine it with @romainl answer and you can do whatever you want.

Example: Let's say I want to add a const to the functions which doesn't have them.

    handleChange = value => {

VIM script:

%s/\([ ]*\)\(\w* = .*$\)\&^\(\&\(.*const.*\)\@!.\)*$/\1const \2/gc

Output:

    const handleChange = value => {

Explanation:

\([ ]*\)\(\w* = .*$\)

- get me all lines which has word =

\&

- combine rules

^\(\&\(.*const.*\)\@!.\)*$

- the line shouldn't contain const.

ATOzTOA
  • 131
  • 3