我有这个文件
foo
foo bar
foo bar baz
bar baz
foo baz
baz bar
bar
baz
foo 42
foo bar 42 baz
baz 42
我想
foo
且不包含bar
foo
且不包含bar
我在某处找不到(找不到链接)我必须使用:exec |
为此。
我尝试了以下操作,但它不起作用
: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
当然,如果我不能选择一行,我就无法在其上执行normal dd
。
有什么想法吗?
赏金注意事项:
我正在寻找一个使用正确的:g和:v命令的解决方案,并且不使用正则表达式黑客,因为条件可能不一样(我可以有2个包含,3个排除)。
另请注意,最后两个不起作用的例子,它们只能删除行,但是当我运行它们而不删除(即查看所选行)时它们返回不正确的信息,它们表现为如上所述。
答案 0 :(得分:10)
我不是vim向导,但是如果您要做的只是“删除包含foo且不包含bar的行”,那么这应该做(我试过你的示例文件):
:v /bar/s/.*foo.*//
编辑:实际上这会留下空行。您可能希望为第二种搜索模式添加可选换行符。
答案 1 :(得分:4)
这可能仍然是你的hackish,但你可以编写一些vimscript来为此创建一个函数和专门的命令。例如:
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
这会创建一个命令,允许您自动执行“正则表达式黑客攻击”。这样你可以做到
:G +foo -bar
用foo获取所有行而不是bar。如果参数不以+
或-
开头,那么它将被视为添加到:g
命令末尾的命令。所以你也可以这样做
:G d +foo -bar
删除行,甚至
:G norm\ foXp +two\ foos -bar
如果你逃离你的空间。它也需要:1,3G +etc
之类的范围,你可以在搜索词中使用正则表达式,但你必须逃避你的空间。希望这会有所帮助。
答案 2 :(得分:3)
这是正则表达式有点麻烦的地方。您需要使用零宽度匹配\(search_string\)\@=
。如果您想以任何顺序匹配项目列表,search_string
应该以{{1}}开头(因此匹配从每行的开头开始)。要匹配不存在的情况,请改用.*
。
我认为这些命令应该做你想要的(为了清楚起见我使用\@!
作为分隔符,而不是通常的#
):
选择包含foo和bar的行:
/
选择包含foo,bar和baz的行
:g#\(.*foo\)\@=\(.*bar\)\@=
选择包含foo并且不包含栏的行
:g#\(.*foo\)\@=\(.*bar\)\@=\(.*baz\)\@=
删除包含foo和bar的行
:g#\(.*foo\)\@=\(.*bar\)\@!
删除包含foo但不包含bar
的行 :g#\(.*foo\)\@=\(.*bar\)\@=#d
答案 3 :(得分:2)
有人可能认为像:g/foo/v/bar/d
这样的组合会起作用,但不幸的是,这是不可能的,你将不得不重新提出一个拟议的解决办法。
如帮助中所述,:global
命令在幕后工作分为两个阶段,
出于兴趣,我查看了Vim源代码中的相关部分:在 ex_cmds.c ,ex_global()
中,您会发现全局标记global_busy
防止命令在忙碌时重复执行。
答案 4 :(得分:2)
除非您愿意使用某些正则表达式,否则您将无法达到您的要求,因为表达式驱动:global
并且它与:vglobal
相反。
这不是黑客攻击,而是命令应该如何工作:他们需要一个表达式来处理。如果你不愿意使用正则表达式,我恐怕你无法实现它。
如果您不愿意使用任何正则表达式,请在此处终止。
假设我们是一个心胸开阔的好人,我们需要一个正则表达式,当一行包含foo
而不是 bar
时,这是正确的。
number 5的建议Prince Goulash完全存在但如果在foo
之后发生bar
则无效。
此表达式完成工作(即打印所有行):
:g/^\(.*\<bar\>\)\@!\(.*\<foo\>\)\@=/
如果要删除它们,请添加d
elete命令:
:g/^\(.*\<bar\>\)\@!\(.*\<foo\>\)\@=/d
说明
这两种模式也可以互换:
:g/^\(.*\<foo\>\)\@=\(.*\<bar\>\)\@!/
产生相同的结果。
使用以下输入进行测试:
01 foo
02 foo bar
03 foo bar baz
04 bar baz
05 foo baz
06 baz bar
07 bar
08 baz
09 foo 42
10 foo bar 42 baz
11 42 foo baz
12 42 foo bar
13 42 bar foo
14 baz 42
15 baz foo
16 bar foo
关于多个包含/排除:
每个排除都由模式
组成\(.*\<what_to_exclude\>\)\@!
每个 include 都由模式
组成\(.*\<what_to_include\>\)\@=
打印包含foo
但不包含bar
或baz
的所有行:
g/^\(.*\<bar\>\)\@!\(.*\<baz\>\)\@!\(.*\<foo\>\)\@=/
打印包含foo
和42
但不包括bar
和baz
的所有行:
g/^\(.*\<bar\>\)\@!\(.*\<baz\>\)\@!\(.*\<foo\>\)\@=\(.*\<42\>\)\@=/
包含和排除的顺序并不重要,您甚至可以混合它们:
g/^\(.*\<bar\>\)\@!\(.*\<42\>\)\@=\(.*\<baz\>\)\@!\(.*\<foo\>\)\@=/
答案 5 :(得分:0)
您希望采用负面展望。本文或多或少地提供了您尝试实现的具体示例。 http://www.littletechtips.com/2009/09/vim-negative-match-using-negative-look.html
我把它改成了 :(。*巴)!克/富\ @ / d
如果您认为这是一个正则表达式黑客,请告诉我们。
答案 6 :(得分:0)
我会戴上帽子。由于vim的文档明确指出递归全局命令是无效的,并且正则表达式解决方案将很快变得非常毛茸茸,我认为这是自定义函数和命令的工作。我创建了:G
命令。
用法为:G
,后跟/
包围的模式。任何不匹配的模式都以!
为前缀。
:G /foo/ !/bar/ d
这将删除与/foo/
匹配且与/bar/
:G /42 baz/ !/bar/ norm A$
这会将$
附加到与/42 baz/
匹配且与/bar/
不匹配的所有行
:G /foo/ !/bar/ !/baz/ d
这将删除与/foo/
匹配且与/bar/
不匹配且与/baz/
不匹配的所有行
:G
命令的脚本如下:
function! s:ManyGlobal(args) range
let lnums = {}
let patterns = []
let cmd = ''
let threshold = 0
let regex = '\m^\s*\(!\|v\)\=/.\{-}\%(\\\)\@<!/\s\+'
let args = a:args
while args =~ regex
let pat = matchstr(args, regex)
let pat = substitute(pat, '\m^\s*\ze/', '', '')
call add(patterns, pat)
let args = substitute(args, regex, '', '')
endwhile
if args =~ '\s*'
let cmd = 'nu'
else
let cmd = args
endif
for p in patterns
if p =~ '^(!\|v)'
let op = '-'
else
let op = '+'
let threshold += 1
endif
let marker = "let l:lnums[line('.')] = get(l:lnums, line('.'), 0)" . op . "1"
exe a:firstline . "," . a:lastline . "g" . substitute(p, '^(!\|v)', '', '') . marker
endfor
let patterns = []
for k in keys(lnums)
if threshold == lnums[k]
call add(patterns, '\%' . k . 'l')
endif
endfor
exe a:firstline . "," . a:lastline . "g/\m" . join(patterns, '\|') . "/ " . cmd
endfunction
command! -nargs=+ -range=% G <line1>,<line2>call <SID>ManyGlobal(<q-args>)
该函数基本上解析出参数,然后分别用每个给定的模式标记所有匹配的行。然后在标记了适当次数的每一行上执行给定的命令。
答案 7 :(得分:0)
好的,这是实际模拟全局命令的递归使用的一个。它允许您至少在理论上组合任意数量的:g
命令。但我警告你,它不漂亮!
我使用Unix程序 nl (请耐心等我!)插入行号,但您也可以使用pure Vim。
:%!nl -b a
:exec 'norm! qaq'|exec '.,$g/foo/d A'|exec 'norm! G"apddqaq'|exec '.,$v/bar/d'|%sort|%s/\v^\s*\d+\s*
完成!让我们看看解释和一般解决方案。
这是我选择的方法:
使用文件的末尾作为临时空间(:g/foo/m$
和类似的)是一个非常着名的技巧(你可以在着名的answer number one中找到它)。另请注意,:g
保留了行的相对排序 - 这是至关重要的。我们走了:
准备:数字行,清除“累加器”寄存器 a 。
:%!nl
qaq
迭代位:
:execute
全局命令,通过将匹配行附加到带有:d A
的累加器寄存器来收集匹配行。.,$
(临时空间,或在我们的情况下,“匹配”空间)这是一个扩展示例:删除包含'foo'的行,不包含'bar',包含'42'(仅用于演示)。
:exec '.,$g/foo/d A' | exec 'norm! G"apddqaq' | exec '.,$v/bar/d A' | exec 'norm! G"apddqaq' | exec '.,$g/42/d A' | exec 'norm! G"apddqaq'
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
(this is the repeating bit)
当迭代位结束时,行.,$
包含匹配以方便您使用。您可以删除它们(dVG
)或其他任何内容。
清理:排序,删除行号。
:%sort
:%s/\v^\s*\d+\s*
我相信其他人可以提高解决方案的细节,但如果你确实需要多个组合:g
S和:v
s转换一个,这似乎是最有前途的解决方案
答案 8 :(得分:0)
内置解决方案看起来非常复杂。 一种简单的方法是使用LogiPat插件:
Doc:http://www.drchip.org/astronaut/vim/doc/LogiPat.txt.html
插件:http://www.drchip.org/astronaut/vim/index.html#LOGIPAT
有了这个,您可以轻松搜索模式。 例如,要搜索包含foo而不是bar的行,请使用:
:LogiPat "foo"&!("bar")
这将突出显示与逻辑模式匹配的所有行(如果已设置hls)。 这样你可以反复检查你是否得到了正确的行,然后用'n'遍历,如果你愿意,用'dd'删除。
答案 9 :(得分:0)
我意识到您明确表示您需要使用:g
和:v
的解决方案,但我坚信这是您应该使用外部工具的完美示例。
:%!awk '\!/foo/ || /bar/'
没有必要重新发明轮子。
答案 10 :(得分:0)
选择包含foo的行并且不包含bar
删除包含foo但不包含bar
的行
这可以通过组合global
和substitute
命令来完成:
:v/bar/s/.*foo.*//g