The context

I store all my shell scripts insde the $HOME/.bin directory. Because of this, I've been looking for a way to set the sh filetype whenever I open a file located in that directory. So, I've created the following autocommand

$ cat ~/.vim/ftdetect/sh.vim
au BufNewFile,BufRead $HOME/.bin/* setf sh

which is stored inside the ~/.vim/ftdetect/sh.vim file. I wrote that command because I found the following statements in the vim help (:h autocmd-patterns)

2. When there is a '/' in the pattern, Vim checks for a match against both the
   short file name (as you typed it) and the full file name (after expanding
   it to a full path and resolving symbolic links).
Environment variables can be used in a pattern: >
    :autocmd BufRead $VIMRUNTIME/doc/*.txt  set expandtab

The problem

When editing a file from the $HOME/.bin directory, the sh filetype is not assigned.

The question

What I am doing wrong here?

Additional context

The ~/.bin directory is a symbolic link (see below) but I don't think this might be causing this

$ test -h ~/.bin && echo $?

2 Answers 2


Your problem is due to :setf[iletype] command. From :h :setf

Set the 'filetype' option to {filetype}, but only if not done yet in a sequence of (nested) autocommands.

As your autocmd is surely run after all the bundled stuff, the filetype for the current buffer might be set already. Then your command has no effect. Use plain :set filetype=sh if you're forcefully going to redefine the filetype (however note that it may result in two consecutive FileType events for the same buffer).

  • I've changed the presented command to au BufNewFile,BufRead $HOME/.bin/* set ft=sh and the filetype is properly assigned. Thanks! However, what seems odd to me is that setf didn't set the filetype even when the filetype hasn't been assigned yet to the opened buffer. For isntance, I tried opening a file with name foobar under that directory: it hadn't been assigned a filetype and the setf didn't set it even when there was no filetype assigned. Because of this, I consider the statement only if not done yet in a sequence of (nested) autocommands to be false.
    – gfe
    Commented May 31, 2020 at 21:05
  • 2
    @R.Morales That's strange. It should work in that case. That could be some local problem.
    – Matt
    Commented May 31, 2020 at 21:09

This is more a workaround than a real answer to your question, but it is what I use for this particular case :

augroup FileTypeRefresh
    au BufWrite * if &ft ==# '' | filetype detect | endif
augroup end

This uses filetype detect when no filetype is set (a bit like setfiletype is supposed to behave).

From :h :filetype-detect:

Use this if you started with an empty file and typed text that makes it possible to detect the file type. For example, when you entered this in a shell script: "#!/bin/csh".

Compared to your approach:

  • Caveat: requires typing something to detect the filetype (the shebang for example)
  • Advantage: works for any file, any folder

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.