Vim 7.1.299 で fnameescape() なる関数が追加された。これは :edit などファイル名が必要な箇所でファイル名をエスケープしてくれる関数。
例えば
VimM#1.txt と言うファイルを開きたいとする。この場合、
:edit VimM#1.txt
などとしてもうまく開けない。これは # が代替ファイル名(:h alternate-file)として展開されてしまうため。正しく開くためにはエスケープする必要がある。
:edit VimM\#1.txt
こう言った展開されるパターンはいくつかあり、いちいちエスケープするのは面倒。これをやってくれるのが fnameescape() 関数。
:execute 'edit' fnameescape('VimM#1.txt')
スクリプトで使うことが多いので、実際は 'VimM#1' は変数のことが多い。なので余計エスケープのし忘れが発生しやすい。
こんな感じで、 fnameescape() は便利な関数です。まる。
ところが
fnameescape() も完璧ではなかった。以下の例は Windows で発生。 congratulations!.txt と言うふざけた名前のファイルを開きたいとする。
:execute 'edit' fnameescape('congratulations!.txt')
しかし実際に開いたファイルは…
:echo expand('%') congratulations\!.txt
! がエスケープされてしまった。なんか [新規ディレクトリ] とか出てるし。Windowsだからね。
これではファイル名に ! が入ったファイルをうまく扱えないので、実質 fnameescape() は使えない。もしかしたら他にも余計にエスケープされてしまう文字があるかもしれない。
更に別の問題
コマンドでファイルが与えられるところでは、 全体をバッククォート(`)で囲うことでシェルからファイル名を指定できる(:h backtick-expansion)。
このバックスラッシュは、Linux ではバックスラッシュでエスケープできるが、Windowsではエスケープすることができない。つまり、fnameescape() を使ってもバッククォートで囲われたファイルを開くことが出来ない*1。
解決策
実は全てを解決する策がある。 `=...` を使えばいい(:h `=)。
これはVimの式の評価結果をファイル名として使用する。これを使えば、
:edit `='congratulations!.txt'`
これで問題なく開ける。なんと、 :execute すら要らなくなった。
式ならほぼ何でも書ける
半角スペースやバッククォートすら書ける。
:edit `='`example file`'` :echo expand('%') `example file`
もちろん関数呼び出しやローカル変数などももろもろ使える
" fuzzyfinder.vim より引用。なにやら頑張っているが… execute printf('file `=%s`', string(a:buf_name)) " ↓で問題ない file `=a:buf_name`
注意
もともとの目的なので当然だが、一切の展開が行われなくなるので注意。つまり、 ~ がホームディレクトリに展開されない。
ここは expand() を使って…といいたいところだが、それだと # なども展開されて元も子もないので、この場合は fnamemodify() を使えばよい。
let path = '~/.vimrc' file `=fnamemodify(path, ':p')`
もちろん :cd や :argadd などにも使える
:cd `='~'` " :cd の場合は ~ や -(直前のディレクトリ) を渡しても問題ない模様 :argadd `='text1.txt'` `='text2.txt'` `='text3.txt'` " 複数渡してもまったく問題ない
さらに
- `=...` は Vim 7.1.299 以前でも使える。
- また、 fnameescape() でないと対処できないケースはそう多くない。
結論
fnameescape() ではなく `=...` を使おう!
*1:そんなファイル名使わんとか言う突っ込みはなしの方向で