如何在Vim中删除多个缓冲区?

146
假设我在Vim中打开了多个缓冲区文件。这些文件有*.cpp*.h和一些*.xml。我想通过:bd *.xml关闭所有XML文件,但是Vim不允许这样做(E93: More than one match...)。
有没有办法解决这个问题?
附言:我知道:bd file1 file2 file3可以起作用。所以我能否将*.xml评估为file1.xml file2.xml file3.xml
8个回答

238

你可以使用 <C-a> 来完成所有匹配。所以如果你输入 :bd *.xml 然后按下 <C-a>,vim会自动完成命令为 :bd file1.xml file2.xml file3.xml


12
@Florian <tab> 只能让你循环浏览匹配结果,并将单个条目放入命令行,而 <C-a> 则可以一次性添加所有匹配结果。 - Björn Steinbrink
我的天啊,你说得对!抱歉。当只有一个可能的结果时,选项卡才有效。 - Florian Klein
你如何在tmux上使用vim的<C-a>快捷键? - nabn
5
默认情况下,tmux没有绑定<C-a>快捷键,但如果你进行了配置以使用<C-a>替代<C-b>来模拟screen的话,你还需要配置它来映射例如<C-a>a使得可以将<C-a>传递到运行在tmux内部的程序中。附带tmux的screen-keys.conf文件就是这样做的。 - Björn Steinbrink
12
如果你安装了vim-rsi(我认为,这对于所有的*nix用户来说都是必备的),为了让命令行中的<C-a>以原始方式工作,你应该使用<C-x> <C-a> - kostix
显示剩余2条评论

65
:3,5bd[elete]   

将删除缓冲区中3到5的范围。


8
如果你想要删除单个缓冲区,比如3和5,可以使用:bd 3 5 - subtleseeker

26

你也可以使用以下替代方案:

    :.,$-bd[elete]    " to delete buffers from the current one to last but one
    :%bd[elete]       " to delete all buffers

1
这个很好用。 我执行 :ls 命令来查看缓冲区编号,然后使用 :a,bbd 命令删除从编号 a 到 b 的缓冲区。 - Neaţu Ovidiu Gabriel

8

您可以使用这个。

:exe 'bd '. join(filter(map(copy(range(1, bufnr('$'))), 'bufname(v:val)'), 'v:val =~ "\.xml$"'), ' ')

将其添加到命令中应该非常容易。

function! s:BDExt(ext)
  let buffers = filter(range(1, bufnr('$')), 'buflisted(v:val) && bufname(v:val) =~ "\.'.a:ext.'$"')
  if empty(buffers) |throw "no *.".a:ext." buffer" | endif
  exe 'bd '.join(buffers, ' ')
endfunction

command! -nargs=1 BDExt :call s:BDExt(<f-args>)

我对Vimscript几乎一无所知,但glob()函数怎么样? - Thanh DK
1
glob() 只会给你现有的文件(在你的硬盘上),而不是打开的缓冲区。 - Luc Hermitte
你忘记对缓冲区名称使用 fnameescape() 函数进行转义。 - ZyX
我刚刚检查了 c:/Program files/foo.bar,甚至是 foo.bar.foo,它完美地工作了。如果我使用缓冲区名称,则可能需要使用 fnameescape()。但是我只是检查缓冲区名称是否与给定表达式 \.{ext}$ 匹配:我将缓冲区编号传递给 :bd。我没有任何理由为正则表达式匹配转义任何内容。 - Luc Hermitte

5
尝试下面的脚本。该示例适用于“txt”,根据需要更改为“xml”等。 修改后的缓冲区不会被删除。按\bd以删除缓冲区。
map <Leader>bd :bufdo call <SID>DeleteBufferByExtension("txt")

function!  <SID>DeleteBufferByExtension(strExt)
   if (matchstr(bufname("%"), ".".a:strExt."$") == ".".a:strExt )
      if (! &modified)
         bd
      endif
   endif
endfunction

[编辑] 同样的操作,不使用 :bufdo(如Luc Hermitte在下面的评论中请求的那样)

map <Leader>bd :call <SID>DeleteBufferByExtension("txt")

function!  <SID>DeleteBufferByExtension(strExt)
   let s:bufNr = bufnr("$")
   while s:bufNr > 0
       if buflisted(s:bufNr)
           if (matchstr(bufname(s:bufNr), ".".a:strExt."$") == ".".a:strExt )
              if getbufvar(s:bufNr, '&modified') == 0
                 execute "bd ".s:bufNr
              endif
           endif
       endif
       let s:bufNr = s:bufNr-1
   endwhile
endfunction

1
我不喜欢使用:bufdo,因为它会破坏当前窗口。 - Luc Hermitte

4

自从 Vim 7.4.282 版本起,TAB 只能为您自动补全一个文件名。
使用 <c-a> 可自动补全所有文件名。

您可以直接使用:

bd filetype

接下来只需使用<c-a>,即可方便地完成指定文件类型的所有打开文件。

例如,你有1.xml、2.xml、3.xml和4.xml等四个文件,

bd xml

然后按下<c-a>

vim将为您自动补全,如下所示:

bd 1.xml 2.xml 3.xml 4.xml

您可以按回车键完成命令。

如果您对上述文件进行了更改,请确保执行以下操作:

bd! xml

3

我也一直需要这个功能。这是我在我的vimrc文件中的解决方案。

function! GetBufferList()
    return filter(range(1,bufnr('$')), 'buflisted(v:val)')
endfunction

function! GetMatchingBuffers(pattern)
    return filter(GetBufferList(), 'bufname(v:val) =~ a:pattern')
endfunction

function! WipeMatchingBuffers(pattern)
    let l:matchList = GetMatchingBuffers(a:pattern)

    let l:count = len(l:matchList)
    if l:count < 1
        echo 'No buffers found matching pattern ' . a:pattern
        return
    endif

    if l:count == 1
        let l:suffix = ''
    else
        let l:suffix = 's'
    endif

    exec 'bw ' . join(l:matchList, ' ')

    echo 'Wiped ' . l:count . ' buffer' . l:suffix . '.'
endfunction

command! -nargs=1 BW call WipeMatchingBuffers('<args>')

现在,我只需要执行:BW regex(例如:BW \.cpp$),就可以删除所有路径名中匹配此模式的缓冲区。
如果您想要删除而不是擦除,当然可以将exec 'bw ' . join(l:matchList, ' ')替换为exec 'bd ' . join(l:matchList, ' ')

1
我有时会想,为什么vim不支持在所有地方使用正则表达式(:badd:bdelete:bufdo:bn...)? - puk

2
非常简单:使用:bd[elete]命令。例如,:bd[elete] buf#1 buf#5 buf#3将删除缓冲区1、3和5。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接