ここでは、これまでの説明で触れなかった操作や、 viをより効率的に、格好良く、美しく活用するための小技などを紹介したい。
入力と削除の項等で触れたが、
viで扱うコマンドのほとんどは回数指定して、同じ動作を複数回一気に実行できる。
例えば、5dd
で5行一気に削除できるし、3fa
で現在位置から数えて3つ目のaまでカーソルを移動させる。
これは他にもいろいろ便利に使える機能である。
例えば、40i-<ESC>
と打ってみて欲しい。
おそらく驚くだろうと思うのだが、いきなり線が引かれてしまうはずである。
なんのことはない、-
の入力を40回繰り返しただけである。
罫線のようなものを簡単に引いてみたり、プログラム中でコメントで枠を作ったりする際に重宝する。
入力以外にも、カーソル移動hjkl
も回数指定できる。
3l
で、カーソルが3つ右に動く。
何に使えるんだ、と思うかもしれないが、ヤンクするときに結構便利なのだ。
日本語のヤンクをするときに、移動先の文字を指定するためにわざわざ日本語FEPを起動しなければならず面倒なのだが、
短い文節ならば、文の頭でy「文字数」l
とでも打てばよい。半角/全角キーなど押す必要がなくなる。
これまで登場したexコマンドといえば、ファイルの保存(:w
)、viの終了(:q
)、あとは置換ぐらいだが、
こいつが持つポテンシャルはこんなものではない。
行単位のカットとヤンクについて説明した。
おさらいしておくと、「行数」dd
で指定行数をカット、
dd
をyy
とすれば指定行数のヤンクとなる。
これは、行数がパッと見た目でわかる(最大10行程度だろうか)程度であれば問題のない操作である。 が、例えば「123行目から331行目をカットしたい」となると、いちいち行数を計算するのも面倒だ。
これは、exコマンドでスマートに解決できる。
コマンド | 動作 |
---|---|
:「start」,「end」d<return> |
「start」 行から「end」 行をカットする |
:「start」,「end」y<return> |
「start」 行から「end」 行をヤンクする |
先ほどの例であれば、:123,331d<return>
とすればよい。
置換と同様の行指定である。
と書いてみるとピンとくる人もいるかもしれないが、
他にもこうした行範囲指定ができるコマンドはある。
例えば、:「start」,「end」w ファイル名<return>
のようにすると、
現在開いてファイル中の、指定した範囲の行のみを別ファイルとして保存できる。
他にもあるかもしれない。
編集作業中に、ちょっとした情報を調べるためにコンソールを使いたくなる、ということはよくある。 X-WindowやWindows系OSを使っていれば、もう一つウィンドウを立ち上げれば済むことだが、 そんな便利な物がない環境であれば話は別である。 一度viを終了させて、また再起動させるのも面倒なので、 一時的に編集を中断できれば便利である。
これには、次の3つの方法がある。
:! コマンド<return>
外部コマンドの実行である。例えば、:!pwd<return>
のようにすれば、
カレントディレクトリが表示され、リターンキーを叩くとまた編集画面に戻る。
:sh<return>
新たにシェルを起動する。
このコマンドを実行すると、唐突にviが終了し(したように見える)、コマンドプロンプトが表示される。
あとは普通のコンソールとして扱える。
用が済んだ時点で、ログアウトするとき同様exit
と入力すると
シェルが終了し、再び viの画面が終了直前の状態で復帰する。
:st<return>
これはCtrl+zと同様の効果がある。つまり、viのプロセスを一時停止するのである。
一時停止すれば、viを起動したコンソールに操作が戻る。
再び編集を行いたい場合は、fg
で一時停止を解除すればよい。
複数のファイルを同時に編集したいときに有効である。
一概にどれが良い、とは言えない。適材適所である。
前方参照(back reference)は、viの、というより正規表現エンジンが持つ機能で、 grep, egrep, sed, awk, perlといった正規表現を利用できるツールであればまず間違いなく利用できる操作である。 これを置換操作に適用すると、 編集の効率が飛躍的に向上すること間違いなし。 これがいかなる効果をもつ操作なのか、それは例を見るのが早い。
本項目で紹介する操作を有効利用するためには正規表現をある程度扱える必要があるので、 知らない方は余所のサイトを当たるなどして、簡単に覚えてきていただきたい。
そういうわけで、例を挙げよう。 次のような文章が入力されているファイルがあるとする。
IN THIS ROOM IS IS A STATUE OF A MONSTER WITH THE BODY OF A CHICKEN AND THE HEAD OF A CAT. THE STATUE IS MADE OF BRONZE, AND LIES ON ON AN ONYX PEDESTAL. THERE ARE UNUSUAL RUNES ON A PLAQUE THEREON.
アンダーラインが引いてある部分を見て欲しい。入力ミスをしたのか、同じ単語が2つ並んでしまっている。 これを訂正するために、「同じ単語が2つ並んでいる場合、一方を削除する」 という操作を行いたい。 これを実現するための置換コマンドは、次のような物である。
:%s/ \([A-Z][A-Z]*\) \1 / \1 /g
[A-Z][A-Z]*
は、「一文字以上の大文字アルファベット」にマッチする(※1)。
注目すべきは、これを囲む\( \)
である。
このようにエスケープした丸括弧で囲まれた部分は、
その後に続く検索、置換文字列内で参照することができる(※2)。
\1
の部分がそれである。
これは[A-Z][A-Z]*
でマッチした文字列に置き換えられる。
例えば、[A-Z][A-Z]*
にONがマッチしたときは、置換コマンドは
:%s/ ON ON / ON / g
と指定したことになる。このコマンドは、 先の「同じ単語が2つ並んでいる場合、一方を削除する」という要求を満たしている。 これを実行した結果、
IN THIS ROOM IS A STATUE OF A MONSTER WITH THE BODY OF A CHICKEN AND THE HEAD OF A CAT. THE STATUE IS MADE OF BRONZE, AND LIES ON AN ONYX PEDESTAL. THERE ARE UNUSUAL RUNES ON A PLAQUE THEREON.
と、見事整形されるわけである。
\( \)
は複数個配置できるし、入れ子にもできる。
それらは、\1, \2, \3, ..., \9
を用いて9個まで参照可能である。
この番号は、最も左にある\(
から、1から順に割り当てられる。
例えば、次のように指定した場合。
\(\(Mapiro\(Mahama\)\)\(Diromat\)\)
これは、次のように割り当てられる。
\1 = MapiroMahamaDiromat
\2 = MapiroMahama
\3 = Mahama
\4 = Diromat
なお、検索文字列全体は、&
で参照することができる。
つまりは、
& = MapiroMahamaDiromat
\1 = MapiroMahamaDiromat
\2 = MapiroMahama
\3 = Mahama
\4 = Diromat
前方参照を用いると、HTMLのソースをブラウザでそのまま表示させるために、
< >
を< >
に一括置換する(※3)とか・・・。
:%s/<\([^>]*\)>/\<\1\>/g
bashで書いたスクリプト内で環境変数設定をしている行「ENV_HOGE=nomean」
を全て「export ENV_HOGE=nomean」
という書式
に一気に書き換えたりとか・・・。
:%s/^[^ ;]*=[^ ;]*$/export &/
簡単にできてしまうわけである。
※1 正規表現に馴染んでいる方は[A-Z][A-Z]*
ってなんじゃい?
と思われたことだろう。本来、[A-Z]+
と書けば済むものである。
しかし、viは拡張正規表現に対応しておらず、+
はまさに拡張正規表現なので使うことが出来ない。
こう書く以外にないのである。ただし、vimであれば[A-Z]\+
のようにエスケープすることで使用できる。
※2 モノによっては\( \)
でなく、普通に( )
を使う場合もある。
この場合、単なる検索文字列の一部として( )
を使う場合にエスケープさせることになる。
※3 よもや、ソース中で、タグ以外の用途で< >
を直接打ち込んではいまいな!
蛇足だが、JavaDocコメントも最終的にはHTMLソースとして使用されるのだから、ちゃんと< >
と書くべきである。
それをうっかり忘れて(知らない、というのは論外)大なり小なりの比較演算子として直書きしているのをよく見かける。