読者です 読者をやめる 読者になる 読者になる

Vimからアプリケーションをバックグラウンドで起動する優れた1つの方法

※この記事はVim Advent Calendar 2012の262日目の記事です。


以前このような記事を書きました。

Vimからアプリケーションをバックグラウンドで起動するいくつかの方法 - Humanity


その後、vital.vimのProcessモジュールにspawn()関数として実装しました。
もちろんWindowsと*nix系環境(Mac含む)をサポートしています。
そして紆余曲折あり、現時点で結果このような実装になりました。
(Processモジュールの最新版はこちらです)

" Execute program in the background from Vim.
" Return an empty string always.
"
" If a:expr is a List, shellescape() each argument.
" If a:expr is a String, the arguments are passed as-is.
"
" Windows:
" Using :!start , execute program without via cmd.exe.
" Spawning 'expr' with 'noshellslash'
" keep special characters from unwanted expansion.
" (see :help shellescape())
"
" Unix:
" using :! , execute program in the background by shell.
function! s:spawn(expr)
  if s:is_windows
    let shellslash = &l:shellslash
    setlocal noshellslash
  endif
  try
    if type(a:expr) is type([])
      let special = 1
      let cmdline = join(map(a:expr, 'shellescape(v:val, special)'), ' ')
    elseif type(a:expr) is type("")
      let cmdline = a:expr
    else
      throw 'Process.spawn(): invalid argument (value type:'.type(a:expr).')'
    endif
    if s:is_windows
      silent execute '!start' cmdline
    else
      silent execute '!' cmdline '&'
    endif
  finally
    if s:is_windows
      let &l:shellslash = shellslash
    endif
  endtry
  return ''
endfunction

注意点

  • shellescape() の第2引数には0でない値を渡すこと
    • これは!や%などのVimにとって特別な文字をバックスラッシュでエスケープし、「:!」コマンドによって展開されてしまうのを防ぎます。
    • 詳しくは :help shellescape()
  • shellescape() を呼ぶ前に「set noshellslash」すること
    • これがないと「:!」コマンドが、引数の中の「\」を「/」に自動的に変換してくれやがります
    • shellslash はパスの区切りとして '\' が使われるときだけ有効なオプション*1なので、「exists('+shellslash')」の式が1を返すかどうかチェックする必要があります

また前回にも書いた通り、

  • Windowsでは「:!start」コマンドを使うことでcmd.exeを介さずプログラムを実行することができ、cmd.exeのブラックボックスで不可解な引数処理を意識せずに済みます
  • *nix系環境ではshellの存在を仮定していいので、shellの力を借りてバックグラウンド実行ができます
    • shellescape()は!や%などのVimにとって特別な文字のみならず、コマンドライン引数のエスケープも行ってくれます

結論

外部プログラム実行には多くの罠があるのでvitalを使いましょう。
そしてバグがあれば報告することで、よりvitalの安心感が増します。
またProcessモジュールだけでなく、vimprocをバックエンドとした、よりリッチなProcessManagerモジュールなどもあります。
詳しくはVim Advent Calendarのujihisaさんの記事一覧を参照してください。

*1:つまり、MS-DOSWindowsOS/2以外でset noshellslashするとエラーになります