Vimからシェルコマンドを実行する方法(:! と :terminal)
ちょっとgitのステータスを確認したいだけなのに、Vimを一度サスペンドしてシェルに戻り、またvimで開き直す、という無駄な往復をしていた時期があります。Vimの中から直接コマンドを実行する方法を覚えれば、その往復はいらなくなります。
目次(10項目)
1行だけ実行する::!
:!{コマンド}
コマンドラインモードで!から始めると、続く文字列をOSコマンドとして実行できます。LinuxやMacでは実行結果がVimの中に表示され、Windowsではコマンドプロンプトが別途起動します。単発のコマンドをさっと実行して結果だけ確認したいときに向いています。
ターミナルとして使う::terminal
:terminal
:terminal {コマンド}
Vim 8.1で追加された機能で、Vimのウィンドウの中に端末エミュレータを開けます。:!が単発のコマンド実行に向いているのに対し、:terminalはシェルセッションそのものをウィンドウとして持てるので、複数のコマンドを連続で打ちたいときや、ビルドプロセスを流しながら別のウィンドウでコードを編集したいときに向いています。ウィンドウの分割・移動は通常のVimのウィンドウ操作とまったく同じ感覚で扱えます。
言語処理系を直接呼び出す
特定の言語のインタプリタが有効になっていれば、その言語のコードを直接実行することもできます。
:python3 {コード}
:ruby {コード}
:perl {コード}
Vim scriptの中でちょっとした計算をPython側に任せたいときなど、ごく限定的な場面で使う機能です。日常的な用途というよりは、プラグイン開発や高度なカスタマイズをする人向けの選択肢と考えられます。
選択範囲をコマンドに渡す
ビジュアルモードで範囲を選択した状態で:!を実行すると、選択範囲をコマンドへの入力として渡し、その出力で選択範囲を置き換えることができます。
banana
apple
cherry
:'<,'>!sortapple banana cherry
選択した行をsortコマンドに通して並べ替えた例です。Vimの中で完結させたい単純な並べ替えなら:sortコマンドでも足りますが、シェルの豊富なテキスト処理コマンド(uniqやawkなど)をそのまま使いたいときは、この方法が手っ取り早くなります。
使い分けの目安
| 状況 | おすすめ |
|---|---|
| 1回だけコマンドを実行して結果を見たい | :! |
| 複数コマンドを対話的に実行したい | :terminal |
| コマンドの出力をバッファに取り込みたい | :r!(別記事) |
:terminalを知る前は、ビルドの実行結果を見るためだけにウィンドウを切り替えていました。ウィンドウ分割の中にターミナルを1枚常駐させる運用に変えてからは、エディタとターミナルを行き来する手間そのものが消えて、地味ですが積み重なると大きい時間短縮になりました。
出力をバッファに取り込む
:! は結果を画面に出すだけで、バッファには残りません。出力をそのままファイルに書き込みたいときは :r ! を使います。カーソル行の下に実行結果が挿入されます。
:r !date " 実行結果をカーソル行の下に挿入
:r !git log --oneline -5
:%!sort " バッファ全体を sort に通して置き換える
:'<,'>!column -t " 選択範囲を整形して置き換える
:%! の形はバッファの中身を外部コマンドの標準入力に渡し、その出力で置き換えます。JSONを整形する、列を揃える、重複を消すといった処理を、Vimの機能を覚えずに手持ちのコマンドで済ませられます。失敗しても u で戻せます。
Vimを一時的に抜ける
いくつもコマンドを打ちたいときは、Vimを閉じずに Ctrl-z で背後に回せます。シェルに戻って作業したあと fg と打てば、開いていたファイルもカーソル位置もそのまま復帰します。:suspend と打っても同じです。
:terminal が使える環境ではそちらのほうが行き来しやすいのですが、画面の狭い端末や古いVimでは Ctrl-z が確実です。サーバー上で素のVimを使う機会がある人は、こちらも覚えておくと詰みません。
使われるシェルを確かめる
:! が呼ぶのは shell オプションに設定されたシェルです。手元のログイン用シェルと違うものが設定されていると、エイリアスや関数が使えず「端末では動くのにVimからは動かない」という食い違いが起きます。
:set shell? " 使われるシェルを確認する
:set shellcmdflag? " 渡されるオプションを確認する
そもそも対話的シェルではないので、.zshrc や .bashrc は読み込まれません。エイリアスに頼ったコマンドではなく、実体のコマンド名で書くのが確実です。
まとめ
単発の確認なら:!、対話的な作業なら:terminalと使い分ければ、Vimを閉じずに済む場面が大きく増えます。コマンド出力をバッファに取り込みたい場合は別記事を参照してください。端末ウインドウ側の話(2つのモードの切り替え、ジョブが動いているときの終了、'termwinkey'の衝突)は:terminalの使い方にまとめてあります。実行結果をバッファに取り込んで加工したいときは、:read !のほうが扱いやすくなります。