I would like to have a file that contains all files that I opened with vim in the past. I know that there is the viminfo file but I want to have just a file with the filenames.

For this I would like to write the list of open buffers with autocmd VimLeave to a file.

I tried :ls | w >> test.txt but this does not work correctly.

  • I know that there is the viminfo file Do you also know that there's :oldfiles command? Doesn't it make the whole idea of having "an own file" useless?
    – Matt
    Nov 21, 2019 at 10:17
  • 4
    That does sound like a XY problem maybe you should ask a question about what you want to do with this list of files and there is probably a better solution to use it than writting it by yourself :)
    – statox
    Nov 21, 2019 at 10:26
  • 1
    I wonder if sessions would be useful to you
    – D. Ben Knoble
    Nov 21, 2019 at 13:38

3 Answers 3


You can have all buffer names that haven't been unloaded (or wiped out?) with:

let files = filter(map(range(1,bufnr('$')), 'bufname(v:val)'), '!empty(v:val)')

You can save it with

call writefile(files, '/path/to/filename.txt')

Try using redir command (:h redir)

For example:

func! MyRedir() abort
    redir! > ~/test.txt
    redir END

augroup save_files | au!
    au VimLeave * call MyRedir()
augroup end

  • Or oldfiles instead of ls
    – D. Ben Knoble
    Nov 21, 2019 at 13:37
  • 2
    There's also execute(), which I often find more convenient than :redir, especially for a single command...
    – filbranden
    Nov 21, 2019 at 15:43

I took Luc Hermitte's helpful answer and improved it somewhat.

  1. It was returning unlisted (previously deleted) buffers, which isn't what I wanted. It now filters on buflisted(v:val), rather than on !empty(v:val).
  2. I've added two commands:
    • Lssave: saves the unsorted buffer list as-is
    • LSsave: saves a sorted version of the buffer list
  3. mkdir the output file's path, in case it doesn't already exist.
function s:bufferList()
    let l:bufferRange = range(1,bufnr('$'))
    let l:bufferList = map(l:bufferRange, 'bufname(v:val)')
    let l:bufferListListed = filter(l:bufferList, 'buflisted(v:val)')
    return l:bufferListListed

command Lssave
    \ let s:bufferList = s:bufferList() |
    \ call mkdir("/path/to", "p", 0740) |
    \ call writefile(s:bufferList, "/path/to/filename.txt")

command LSsave
    \ let s:bufferList = s:bufferList() |
    \ let s:bufferListSorted = sort(s:bufferList) |
    \ call mkdir("/path/to", "p", 0740) |
    \ call writefile(s:bufferListSorted, "/path/to/filename.txt")
  • 1
    Are you sure about returning $varname instead of l:varname? Jan 14, 2022 at 0:30
  • 1
    BTW, no need to pollute script variables with a static list of filenames. Also all these functions mutate their parameter. Even if having multiple variables may seem clearer, in the end they will all be the same identical variable, which may lead to confusing debugging sessions. That's why I did everything in one line (with an old syntax; you may assume lambdas nowadays). Jan 14, 2022 at 0:30
  • 1
    An interesting improvement would be to read the pathname as an optional parameter for the command. Jan 14, 2022 at 0:32
  • 1
    You will want to filter on !empty(bufname) && buflisted as unnamed buffers (like the ones we get on new vim sessions, or on :new, :tabnew, and so on) will be listed and don't make sense. With the new syntax, it's (IIRC): range(1, bufnr('$'))->filter({_,v -> buflisted(v) && !empty(bufname(v))})->map({_,v -> bufname(v)}) (untested) Jan 14, 2022 at 0:38
  • 1
    The bufferList() function above can be simplified by using the getbufinfo(#{buflisted: v:true}) call. Refer to vimhelp.org/builtin.txt.html#getbufinfo%28%29 for more information. Jan 14, 2022 at 3:06

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.