3

Let's say I have opened many files on Vim with vim file1 file2 file3... And their content is like the following:

file1:

random content
random content
stringThatMatchesRegex
random content

file2:

random content
random content
random content

file3:

stringThatMatchesRegex
random content
random content
random content

Considering I could have n files opened like that, I'd like to find a way of closing all files that match stringThatMatchesRegex, so I'd like to close undesirable files with a regular expresssion. In this example, only file2 would remain open. I know I could close single files using :bd on Vim, but I have no idea how to implement this if condition. Is it possible? Can I close files that have a line or lines that match a regular expression?

3
  • 1
    Whilst it is likely possible to write a vimscript function to do this, can you expand on your use case? This may be an XY problem; fastidious management of buffers can sometimes, but not always, be a sign of a vim workflow that can be optimised. – Andrew Ho-Lee Apr 13 at 18:46
  • @AndrewHo-Lee Actually, I'm receiving a list of files from an external application, this list could reach hundreds of files inside a specific category. I thought of opening those files with Vim because I can easily edit those files by creating a macro that goes to the next file... So with a simple macro I end up editing many files at once. I honestly don't know if there's a straightforward way of closing files like that on Vim. If not, perhaps filtering the files before they're opened would be a better solution since I'm already receiving this list inside a bash script. – raylight Apr 13 at 19:06
  • 1
    I suspect that filtering before you try and open in vim would be the more sane way of doing it. For example, to open all files in a directory that do not contain the string not_me, you could do vim `find . -type f -exec grep -qvs 'not_me' {} \; -print` – Andrew Ho-Lee Apr 13 at 22:19
6

One way is to populate the quickfix list with matches then use :cfdo to execute :bdelete

:vimgrep /pat/ ##
:cfdo bdelete

Even though the files are closed the files are still in the argument-list. Remove them via :argdelete with % to represent the current file

:vimgrep /pat/ ##
:cfdo argdelete % | bdelete

Additionally, I think it might be worth looking into :Cfilter and the quickfix list to do a series of manipulations. Use :cdo/:cfdo to run commands on the given buffers. Use :cnext/:cprev/:cnfile/:cpfile to navigate between files. Filter with :Cfilter

:cexpr argv()
:packadd cfilter
:Cfilter! /pat/

If you are going to be cycling buffers by hand I would suggest some mappings for :next/:prev. It would also be good to have mappings for the quickfix list. I use Tim Pope's vim-unimpaired. Here are some example mappings:

nnoremap [A :first<cr>
nnoremap [a :prev<cr>
nnoremap ]a :next<cr>
nnoremap ]A :last<cr>
nnoremap [Q :cfirst<cr>
nnoremap [q :cprev<cr>
nnoremap [<c-q> :cpfile<cr>
nnoremap ]<c-q> :cnfile<cr>
nnoremap ]q :cnext<cr>
nnoremap ]Q :clast<cr>

For more help see:

:h :vimgrep
:h :grep
:h :cfdo
:h :bdelete
:h :_##
:h :argdelete
:h :_%
:h :cnfile
:h :cexpr
:h argv()
:h :Cfilter
:h :packadd
6
  • According to what I read on :h this answer should work. But in my case the :bd or :bdelete: is not working as I expected. If I finish this logic and then I use :n and :N I'm still capable of going forward and backwards through the files that should be closed.... There's still something missing. – raylight Apr 13 at 21:37
  • Of course you can use :next/:prev to advance to the next file in the Argument List. It will simply re-open the file. If you want to remove a file from the arglist you need to do something like :argdelete. e.g. :cfdo bd | .argdelete – Peter Rincker Apr 13 at 21:39
  • I'm sorry, but this answer doesn't work. Have you tested this answer with three files as I show in my example? When I make it with 3 files it seems to work, but if I add a fourth file that doesn't match the regex it doesn't work anymore. In my specific case, I can't assume that I'll have only one file that doesn't match the regex. My example was with three files but the number of files that doesn't match the regex will vary, as well as the number of files that match the regex... – raylight Apr 17 at 11:18
  • @raylight perhaps you could explain what happens when it doesnt work? – D. Ben Knoble Apr 17 at 13:07
  • I have ran a test. It is best to do the :argdelete % before the :bdelete, so that % represents the current filename. Although I think removing from the arglist is enough and the :bd is unnecessary for this task – Peter Rincker Apr 17 at 13:39
0

In tab view it is generally easy to see open tabs. Please open files in tabs:

'vim -p <file list>'

Run regex and commands as:

:tabdo g:stringThatMatchesRegex: | q

Same can be done with :cfdo instead of (bd) buffer deletion you need to close the file using (q)

:cfdo g:stringThatMatchesRegex: | q
5
  • 1
    Welcome to Vi and Vim! I suppose instead of :quit you could use :tabclose here; also, why the bar | after g/regex/? – D. Ben Knoble Apr 17 at 16:32
  • Hi, bar is for separating >1 commands. And quit or tab close both should work. I prefer q as it is short. – mangupt Apr 17 at 16:44
  • Also if you are not in tabs mode. then q will close your buffer that's what was wanted. – mangupt Apr 17 at 16:47
  • 1
    Ah, I misunderstood that the bar was separating the global command (which defaults to printing) from the quit, so that both were arguments to tabdo/cfdo; for some reason I thought you had intended to run :g/pat/quit on each tab, which (in reflection) makes little sense. Some edits to clarify might be nice. Note that you can get inline code in markdown by surrounding text with single backticks (`) – D. Ben Knoble Apr 17 at 16:55
  • 2
    Thanks for suggesting. I edited the answer and added space. – mangupt Apr 17 at 17:03

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.