Multiple :g and :v commands in one statement

I have this file

foo
foo bar
foo bar baz
bar baz
foo baz
baz bar
bar
baz
foo 42
foo bar 42 baz
baz 42

I want to

  • Select lines which contain foo and do NOT contain bar
  • Delete lines which contain foo and do NOT contain bar
  • I read somewhere (can't find the link) that I have to use :exec with | for this.

    I tried the following, but it doesn't work

    :exec "g/foo" # works
    :exec "g/foo" | exec "g/bar" -- first returns lines with foo, then with bar
    :exec "g/foo" | :g/bar -- same as above
    

    And ofcourse if I cannot select a line, I cannot execute normal dd on it.

    Any ideas?

    Edit

    Note for the bounty:

    I'm looking for a solution that uses proper :g and :v commands, and does not use regex hacks, as the conditions may not be the same (I can have 2 includes, 3 excludes).

    Also note that the last 2 examples of things that don't work, they do work for just deleting the lines, but they return incorrect information when I run them without deleting (ie, viewing the selected lines) and they behave as mentioned above.


    This might still be hackish to you, but you can write some vimscript to make a function and specialized command for this. For example:

    command! -nargs=* -range=% G <line1>,<line2>call MultiG(<f-args>)
    fun! MultiG(...) range
       let pattern = ""
       let command = ""
       for i in a:000
          if i[0] == "-"
             let pattern .= "(.*<".strpart(i,1).">)@!"
          elseif i[0] == "+"
             let pattern .= "(.*<".strpart(i,1).">)@="
          else
             let command = i
          endif
       endfor
       exe a:firstline.",".a:lastline."g/".pattern."/".command
    endfun
    

    This creates a command that allows you to automate the "regex hack". This way you could do

    :G +foo -bar
    

    to get all lines with foo and not bar. If an argument doesn't start with + or - then it is considered the command to add on to the end of the :g command. So you could also do

    :G d +foo -bar
    

    to delete the lines, or even

    :G norm foXp +two foos -bar
    

    if you escape your spaces. It also takes a range like :1,3G +etc , and you can use regex in the search terms but you must escape your spaces. Hope this helps.


    I'm no vim wizard, but if all you want to do is "Delete lines which contain foo and do NOT contain bar" then this should do (I tried on your example file):

    :v /bar/s/.*foo.*//
    

    EDIT: actually this leaves empty lines behind. You probably want to add an optional newline to that second search pattern.


    This is where regular expressions get a bit cumbersome. You need to use the zero width match (search_string)@= . If you want to match a list of items in any order, the search_string should start with .* (so the match starts from the start of the line each time). To match a non-occurrence, use @! instead.

    I think these commands should do what you want (for clarity I am using # as the delimiter, rather than the usual / ):

  • Select lines which contain foo and bar:

    :g#(.*foo)@=(.*bar)@=

  • Select lines which contain foo, bar and baz

    :g#(.*foo)@=(.*bar)@=(.*baz)@=

  • Select lines which contain foo and do NOT contain bar

    :g#(.*foo)@=(.*bar)@!

  • Delete lines which contain foo and bar

    :g#(.*foo)@=(.*bar)@=#d

  • Delete lines which contain foo and do NOT contain bar

    :g#(.*foo)@=(.*bar)@!#d

  • 链接地址: http://www.djcxy.com/p/49504.html

    上一篇: 复制并粘贴到vi中

    下一篇: 多个:g和:v命令在一个语句中