`vimproc#system({expr})`の{expr}の展開のされ方

この記事は[Vim Advent Calendar 2012][8] : ATND231日目の記事になります。


昨日の記事は@[ujihisa][1]さんの[Vim script初心者用入門記事][3]でした。
なので、今回はVim scripterになると必ずお世話になるだろう[vimproc.vim][4]、
具体的には`vimproc#system({expr})`の{expr}の展開のされ方について説明します。

--------------------------------------------------------------------------

この記事で語ることは、基本的に`vimproc#system()`でのみで確認しています。
`vimproc#system_bg()`などでも同じ展開のされ方になると思いますが(たぶん)、
未確認なのでご了承ください。

以下のVim環境で確認しています。

* Windows7 Pro SP1 64bit
* VIM - Vi IMproved 7.3 MS-Windows 32-bit GUI version Included patches: 1-966
* `set shell=C:\Windows\System32\cmd.exe`
* `set noshellslash`
* vimproc: `c7bada0`

--------------------------------------------------------------------------

# {expr}の展開の流れ

`vimproc#system({expr})`の{expr}に指定された文字列は大まかに以下の順番で展開されます。
(実際にはもうちょっと複雑で、以下の通りではありません。が、処理は以下のようなことが処理されています。)

1. コメント除去
2. 複数のステートメントに分解する(アンパサンド、パイプ、セミコロン)

3. 特殊文字を展開する

    1. ブロック
    2. チルダ
    3. 実行コマンドパス
    4. 変数
    5. ワイルドカード
    6. チルダ

4. コメント除去

5. 複数のステートメントに分解する(リダイレクト)

6. 各ステートメントの実行


ただし、文字が以下に当てはまる場合には上記とは異なる扱いになります。

* シングルクォート内の文字
* ダブルクォート内の文字
* バッククォート内の文字
* エスケープされている文字


# それぞれの説明

## コメント除去

`#`以降の文字列はすべてコメントととして扱われます。

```vim:
echo vimproc#system('echo hello # world!')
" hello
echo vimproc#system('echo hello $HOGE world!') " $HOGEが'#'のとき
" hello
echo vimproc#system('echo hello # world $HOGE hoge') " $HOGEが'#'のとき
" hello
```

## 複数のステートメントに分解する(アンパサンド、パイプ、セミコロン)

vimprocは`&&`、`||`、`;`によって複数のステートメントだと認識します。
もし変数に`'&&'`などが入っている場合、vimprocは１つのステートメントだと認識します。
(この場合の`'&&'`の処理は処理系に依存します)
また、vimprocは`&`と`|`を単なる文字列として認識します(これも処理系に依存しますね)。

```vim:
echo vimproc#system('echo hello && echo world')
" hello 
" world
echo vimproc#system('echo hello || echo world')
" world
echo vimproc#system('echo hello ; echo world')
" hello
" world
echo vimproc#system('echo hello $HOGE echo world') " $HOGEが'&&'のとき
" hello 
" world
```

## 特殊文字を展開する

### ブロック

vimprocはブロックという値リストのようなものをサポートしています。
(`:h vimshell-tips-block`を参照)

```vim:
echo vimproc#system('echo /bin/{cd,ls}')
" /bin/cd /bin/ls
echo vimproc#system('echo {0..9}')
" 00 01 02 03 04 05 06 07 08 09
echo vimproc#system('echo {00..09}')
" 000 001 002 003 004 005 006 007 008 009
echo vimproc#system('echo {0..3}-{0..3}')
" 0..3}-{0..3
echo vimproc#system('echo /bin/$HOGE') " $HOGEが'{cd,ls}'のとき
" /bin/{cd,ls}
```

### チルダ

` ~`を`$HOME`の値に置換します。(チルダの前のスペースも必要！)

```vim:
echo vimproc#system('echo ~')
" C:/Users/rbtnn
echo vimproc#system('echo a~')
" a~
```

### 実行コマンドパス

` =`に続くコマンド文字列をそのコマンドのパスに置換します。(イコールの前のスペースも必要！)

```vim:
echo vimproc#system('echo  =cmd')
" C:/Windows/SysWOW64/cmd.exe
echo vimproc#system('echo  =ls')
" C:/Git/bin/ls.exe
echo vimproc#system('echo  =ls hello')
" C:/Git/bin/ls.exe hello
```

*https://github.com/Shougo/vimproc.vim/pull/68 要参照*

### 変数

環境変数を展開します。

```vim:
echo vimproc#system('echo $HOME')
" C:/Users/rbtnn
```

### ワイルドカード

vimprocは3種類のワイルドカード(`*`,`?`,`[]`)をサポートしています。
(`:h vimshell-tips-wildcard`を参照)

```vim:
echo vimproc#system('echo ~/ntuser.dat.????')
" C:/Users/rbtnn/ntuser.dat.LOG1 C:/Users/rbtnn/ntuser.dat.LOG2
echo vimproc#system('echo ~/.vim[a-z]c')
" C:/Users/rbtnn/.vimrc
echo vimproc#system('echo ~/.git*')
" C:/Users/rbtnn/.git C:/Users/rbtnn/.gitconfig C:/Users/rbtnn/.gitconfig.local C:/Users/rbtnn/.gitignore
```

## 複数のステートメントに分解する(リダイレクト)

vimprocは5種類のリダイレクト(`<`,`1>`,`2>`,`>&`,`>>`)をサポートしています。


## シングルクォート

vimprocはシングルクォート内のシングルクォートを`''`で表すことができます。変数は展開されません。


```vim:
echo vimproc#system("echo 'He''s a student.'")
" "He's a student."
```

## ダブルクォート

vimprocはダブルクォート内で特定の文字をエスケープすることができ、以下のような辞書(`escape_sequences`)にて変換が行われます。変数は展開されます。

```vim:
let escape_sequences = {
      \ 'a' : "\<C-g>", 'b' : "\<BS>",
      \ 't' : "\<Tab>", 'r' : "\<CR>",
      \ 'n' : "\<LF>",  'e' : "\<Esc>",
      \ '\' : '\',  '?' : '?',
      \ '"' : '"',  "'" : "'",
      \ '`' : '`',  '$' : '$',
      \}
```

```vim:
echo vimproc#system('echo "hoge\nfoo"') " fooが出力されなかった。バグ？
" hoge
"
echo vimproc#system('echo "hoge\tfoo"')
" hoge	foo
echo vimproc#system('echo "\$HOHE"')
" $HOHE
echo vimproc#system('echo "$HOME"') 
" C:/Users/rbtnn
```

## バッククォート

vimprocはバッククォート内にコマンドもしくはVim script式を入れて評価した結果を展開することができます。
(`:h vimshell-tips-backquote`を参照)

```vim:
echo vimproc#system('echo `git --version`')
" git version 1.8.1.msysgit.1
echo vimproc#system('echo `=3`') " Vim script式
" 3
echo vimproc#system('echo `=3*&columns`') " Vim script式
" 510
```

## エスケープ

vimprocはクォート外では以下の4つの文字をエスケープ出来ます。

```vim:
echo vimproc#system('echo \\')
" \
echo vimproc#system('echo \''')
" '
echo vimproc#system('echo \"')
" "\""
echo vimproc#system('echo \`')
" `
```


--------------------------------------------------------------------------

以上、「`vimproc#system({expr})`の{expr}の展開のされ方」でした。
[vimproc.vim][4]は[暗黒美夢王さん][6]のプロダクトなので[vimshell.vim][7]の挙動そのままですね。
明日の記事は@[deris0126][2]さんです。


[1]: https://github.com/ujihisa
[2]: https://github.com/deris
[3]: https://gist.github.com/ujihisa/6027100
[4]: https://github.com/Shougo/vimproc.vim
[5]: https://github.com/rbtnn/vimconsole.vim
[6]: https://twitter.com/ShougoMatsu
[7]: https://github.com/Shougo/vimshell.vim
[8]: http://atnd.org/events/33746