55

Suppose I start Vim to edit a new file in a directory that is not yet created:

vim nonExisitingDirectory/newFile.txt

Vim will happily show me an empty buffer and I can start writing my new file. But when I want to write the file to disk I get this error:

E212: Can't Open file for writing.

I presume this is because the directory does not yet exists. Is there a way to force Vim into creating the directory for me?

6 Answers 6

49

As far as I know there is no setting or some such to do this. But not all is lost, we can of course use the BufWritePre autocommand.

This is executed before the buffer is written to the disk. So we can create the directory there if it doesn't exist yet.

For example:

augroup Mkdir
  autocmd!
  autocmd BufWritePre * call mkdir(expand("<afile>:p:h"), "p")
augroup END
  • <afile> refers to the file we're trying to save; :p is a modifier to expand it to the full pathname (rather than relative), and :h removes the last path component (the file).
  • We then call mkdir() if required. We need the p flag for mkdir() to make all parents directories (i.e. in the case of nonexistent/more_nonexisting/file), which also ensures it won't error out if the directory already exists.

You could, of course, also run the mkdir() command from the Vim commandline, or bind it to a keybind, ie:

nnoremap <Leader>m :call mkdir(expand("%:p:h"), "p")<CR>

Here I used % instead of <afile>, since that's only valid from within an autocommand (% refers to the currently active buffer, which would not work with :wa for example; <afile> refers to the filename of the buffer that triggers the autocmd).

You can also ask for a confirmation before writing a directory if you want. See this question for more details: How can I stop Vim from writing a file in BufWritePre autocommand?


The above snippet will create the directory on the first write (:w). You could, if you wanted, also create the directory when you first open it (i.e. just after typing vim ...) by using the BufNewFile autocmd instead of BufWritePre.


There is also a plugin called auto_mkdir which is effectively the same a the above.

On this page there is a slightly expanded snippet which also asks you if you want to create the directory first, which some may consider to be useful. It also has converts the filename of the encoding before writing it:

call mkdir(iconv(expand("%:p:h"), &encoding, &termencoding), 'p')

I'm not sure if this is actually required though, but if you mix encodings a lot and get weird filenames, you could try it.


I put all of the above in an auto_mkdir2.vim plugin for easier installation.

0
12

I can recommend a vim plugin from Tim Pope called vim-eunuch which defines many extremely useful commands when your work on UNIX/Linux with Vim (check out its features!).

Let's say you open vim with vim this/is/a/test and none of these directories existed before. Just run :Mkdir!<CR> and vim-eunuch creates them for you (with mkdir -p), so you can now save your file with :w<CR>.

12

Another way with a vanilla Vim (without extra conf or plugins). in Vim:

:!mkdir -p /folder/you/want/
:w  #save file

or

$ vim /folder/you/want/myfile.conf
$ ctrl+z # switch to the terminal then create the folders
$ mkdir -p /folder/you/want/
$ fg # return to Vim
$ :w  #save file
1
  • 1
    This should be the accepted answer, as the OP does not ask for a plugin to solve the issue. Aug 18, 2022 at 18:56
3

Use :W to create a file and its parent directories:

function! s:WriteCreatingDirs()
  let l:file=expand("%")
  if empty(getbufvar(bufname("%"), '&buftype')) && l:file !~# '\v^\w+\:\/'
    let dir=fnamemodify(l:file, ':h')
    if !isdirectory(dir)
      call mkdir(dir, 'p')
    endif
  endif
  write
endfunction
command! W call s:WriteCreatingDirs()

(Add to your vimrc. Based on a combination of Zyx's answer and Damien Pollet's answers).

2

I hope to contribute a version that builds on the above answers to streamline the workflow just that much more. I'm often creating new files within a wide range of project structures. This tweak will save me a solid amount of time.

There are two dependencies that I can think of:

  1. I set autochdir because of how it works with <C-x><C-f> for my import statements.
  2. the saveas "leaves behind" (if you will) a hidden buffer at the end of the buffer list. I'm not sure if that is state behavior that all vim/nvim instances enjoy.
  3. Other FYI: I use NVIM on a Mac OS

    " Auto magically Mkdir
    " ====================
    
    autocmd BufWritePre * call MkDir()
    
    function! MkDir()
       if !isdirectory(expand("<afile>:p:h"))
          let confirmation=confirm("Create a new directory?", "&Yes\n&No")
          if confirmation == 1
             call mkdir(expand("<afile>:p:h"), "p")
             lcd %:p:h
             saveas %:t
             echom "Created a new directory:" expand("<afile>:p:h")
             let buf_del = bufnr("$")
             exe "bd" . buf_del
          endif
          redraw
       endif
    endfunction
    
    " Conditionaly create the parent directory when writing to disk
    " <afile> refers to the file we are saving
    " :p is a modifier that expands to full filename
    " :h is a modifier that removes the file from full filename
    " :t is a modifier that generates the filename with extension
    " "p" is the flag that will create all the parent directories
    
0
noremap <leader>w <Cmd>:call mkdir(expand("%:p:h"),"p")<CR>:w<CR>

I use the above mapping to save a file with a new directory path. In above command %:p = "<full path to>nonExisitingDirectory/newFile.txt", :h removes the filename "newFile.txt". After execution of expand() function the command will become :call mkdir("nonExistingDirectory","p") which will create the directory if not existing and it will also create also intermediate non existing directory.

1

Your Answer

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

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