この記事は Vim Advent Calendar 2016 (その2) の3日目の記事です。
UNIXのテキスト処理
UNIXでテキストを自動整形する際、パイプ機能は欠かせない。
$ cat a.txt 1 hoge 2 piyo 3 fuga $ cat a.txt |sed 's/piyo/foo/' |grep '2' 2 foo
シェル上で | というパイプ記号を使ってコマンドを次々繋げることで、複雑なテキスト処理をこなすわけだ。
パイプは便利だが、テキストエディタをパイプとして使う人はあまり見かけない。
テキストエディタ=対話的 という常識があるため、パイプのような自動処理とは相性が悪いと思われているのだろう。
しかし今日はあえて、シェルスクリプトやワンライナーの中にvimを埋め込み、パイプとして静的に使ってみたい。
vimをパイプとして使う
vimを普通に使って冒頭の$ cat a.txt |sed 's/piyo/foo/'を行うには、ノーマルモードでjwCfooと入力すればよい。
このjwCfooという呪文をシェル上で使用するには以下のようにすれば良い。
# 最も普通の方法 $ cat a.txt |vim -es +'norm jwCfoo' +%p +q! /dev/stdin # あるいは $ cat a.txt |ex -s +'norm jwCfoo' +%p +q! /dev/stdin # このやり方は Vim: Reading stdin... 問題が生じる $ cat a.txt |vim - -es +'norm jwCfoo' +%p +q! |sed '1d'
とすればよい。一番上のワンライナーの説明をしておくと
vim -eはexと等価で、vimを非対話(exモード)で起動するという意味。-sはサイレントモードで起動し、標準出力を汚さないというオプション。+'norm jwCfoo'のnormは、ノーマルモードのコマンドを使うという意味。jwCfooはvimの呪文。中にESCを入れるにはCtrl+vしてESCを押す。+%pはファイルの全内容を標準出力に表示するexコマンド。+q!はvimを強制終了するexコマンド。馴染み深い。/dev/stdinは読み込み先を標準入力 (cat a.txt) にするという意味。
三番目のワンライナーは/dev/stdinの代わりにハイフン(標準入力のシンボル)を使っていて、スマートに見える。
ただしこの方法では、標準出力の冒頭にVim: Reading from stdin...というクッソうざい文字列が勝手に挿入される。
こいつを消すには、直後にシェルコマンド|sed '1d'等を噛ます必要がある。
外部コマンドにしよう
以下のようなvipeコマンドを作っておくと、シェル上でvimの呪文が使いやすくなる。
# .bashrcや.zshrcに書き込む vipe () { # コロン':'でESC入力を代替する場合はコメントを外す。^[はCtrl+vしてESC押して入力 # COMMAND=$(echo "$*" |sed -e 's/:/^[/g') vim - -es +":norm gg" +":norm $COMMAND" +:%p +:q! |sed '1d' }
使い方は以下のような感じ。
$ cat a.txt 1 hoge 2 piyo 3 fuga $ cat a.txt |vipe jwCfoo |grep '2' 2 foo # ^[はESC文字で、Ctrl+Vした後にESCを押して入力する $ cat a.txt |vipe Abar^[oxxxx 1 hogebar xxxx 2 piyo 3 fuga # コマンドに空白文字を含む場合は'か"でくくる $ cat a.txt |vipe "A bar baz" 1 hoge bar baz 2 piyo 3 fuga
シェル上でvimのマクロ機能を使う
vimには超便利なマクロ機能が存在する。
簡単に説明すると、ノーマルモードにおける一連の操作をマクロ文字列として記録再生する機能で、
qaと押すことでレジスタaに記録し、@aと押すことで再生できる。
上述のjwCfooという呪文はvimのマクロの一種といえるだろう。
vimを対話的に使って (qaを使って) 記録したマクロを表示するには、ノーマルモードで"apと押せば良い。
こうして表示されたレジスタaの中身は、vipeコマンドの引数として食わせることが出来る。
$ cat a.txt |vipe "レジスタの中身を書き込む"
これで「vimのマクロを使いまわしたいな〜」なんて場合も安心ですね!
参考
vim -esとした時に表示されるVim: Reading from stdin...問題についてはコチラ。
vimのソースコードを修正してくれ~