Last-modified:2016/10/14 21:43:19.
『UNIX今日の技』の vi の操作に関連する項目を纏めたものです。 以下、vi と銘打ってますが、本当は vim です。 まあ、vim の方が高機能なだけで操作方法にあまり違いはありません。
メインエディタは秀丸@windows でも、UNIX 機に転送してしまったファイルを修正したい、ということはままあります。 そーゆー時に一度手元に転送して、修正して、転送するという手間は面倒ですね。 vi は POSIX で定義されており、標準的な UNIX 機なら入っていることが保証されています。 「ちょこっと修正」に vi を使えるようになったら便利ですよ。
vi は「コマンドモード」と「入力モード」の2つのモードを利用します。 最初は以下の程度の認識から始めましょう。
これだけ知ってたら最低限の編集はできますね? では、次回からはもっとラクチンに編集作業するための具体例を実演していきます。
題材として、以下の INCARファイルを編集することを考えます。
# SCF input for VASP
# Note that VASP uses the FIRST occurence of a keyword
SYSTEM = Untitled (VASP)
PREC = High
IBRION = -1
NSW = 0
ENCUT = 400
NELM = 40
NELMIN = 2
EDIFF = 1.0e-05
EDIFFG = -0.02
VOSKOWN = 1
NBLOCK = 1
ISPIN = 1
INIWAV = 1
ISTART = 0
ICHARG = 2
LWAVE = .FALSE.
LCHARG = .FALSE.
ISMEAR = 1
SIGMA = 0.2
IALGO = 48
LREAL = .FALSE.
ENCUT を 500 にする場合、以下のように操作すれば良いでしょう。
コマンドモードで「/string」と打つと、 カーソル位置以降で最初に string にマッチする部分までジャンプします。 vi では検索は、「探すため」というより「移動するため」によく使われます。
項目 ENCUT は INCAR 中でほぼ一回しか出てこないので、 これで大体一発で飛べます。 しかし目的の値の部分にに行くまでに更に数回カーソル移動を繰り返さなければなりません。 今回の INCAR の例では 400 という文字列が他に出て来ないことが分かっているので、 そのまま「/400」でジャンプする方がラクです。
順方向で検索を続ける場合はコマンドモードで「n」、逆方向では「N」です。 叩く度に次にマッチする部分にジャンプします。
なお、最後に行った検索ワードは常に記憶されているので、 何らかの編集を行った後でも「n」や「N」 でジャンプできます。
今回の編集内容は「4→5」に書き換えるということでした。 ならば、4の上で「x→i→5」と打てば目的は達成できます。 しかしこの「削除して挿入」という操作は編集作業で非常によく行われる操作なので、vi では単独コマンドとして「s」に割当てられています。
私としては2キーコマンドが1キーになるだけで十分魅力的なんですが、他の人にはそれほど魅力的には映らないかもしれません。 「s」の威力は、「コマンドの回数を指定する」と組み合わせると最大限に発揮されます。 「コマンドの回数を指定する」は、また後日。
大文字の「I」 で カレント行の先頭にカーソルを移動し、その位置に文字を挿入します。
おそらく、vi で最も便利なのはこの「.」です。 「.」は直前の操作と同じ操作を実行します。 例えば今回の例では「I#[ESC]」と等価になります。
勿論「I」以外にも使えます。 私がよく使うのは、「s(一文字削除&挿入)」「cw(単語削除&挿入)」です。
「=」という文字を 「>」に置換したい場合、以下のようにすることもできます。
「FALSE」を「TRUE」に置換したい場合、以下のようにすることもできます。
単純に「G」とした場合はファイルの末尾へジャンプします。
「G」の前に数字、例えば「12G」とした場合はファイルの12行目へジャンプします。
「gg」と小文字を2回打てば、ファイルの先頭へジャンプします。
カレント行の下に新しい行を追加して入力モードに移行します。
大文字「O」の場合は、カレント行の上に新しい行を追加します。 (すなわち、カレント行の先頭に改行を挿入し、その改行位置で入力モードとなります。)
例えば、60個のコメント文字でテキストを視覚的に区切りたいとしましょう。
############################################################
60数えながら「#」を打つのは嫌ですね。 また、120行だけ下に移動したい場合はどうしましょうか。 120回「j」を打つのは嫌気が差すでしょう。
vi ではコマンド入力の前に数字を打っておくと、その操作をその回数だけ繰り返します。 上記の目的は「60i#[ESC]」や[120j」で達成できます。 その他、既に紹介したコマンドでは以下のように使えます。
undo は「u」、redo は「C-r」です。 「4u」のように回数を指定して遡ることもできます。
「:%s/old/new/」と打ち込むことで、文字列の一括置換ができます。
「/old/」の部分は正規表現が効きます。 失敗しても「u」ですぐ戻せるので試行錯誤的にやっても無問題です。 細かいオプションがあったりするけど省略。
「%」の部分、細かい範囲指定をする場合は下記の『範囲指定「v」』を使うと便利です。
操作に対して範囲を指定したい場合があります。
範囲指定したい部分の先頭で「v」を打ってからカーソルを移動すると「今カーソルがいる地点まで」が範囲になります。 次に「v」を打つか「範囲に対応した編集」をするまでは範囲指定は有効なので、「検索によるジャンプ」や「行番号ジャンプ」など通常の移動手段は何でも使えます。
範囲指定してから以下の操作ができます。
私は置換よりも、主に削除/コピーするのに使います。
カット、削除は「d」キー(delete の意)です。 「d」に続けて様々な指定ができますが、 よく使うのは以下のようなところでしょう。
「d」 を 「y」 に置き替えるとヤンク系の操作になります。
「c」 を使えば修正系の操作になります。 これまででも「cw」は紹介しました。
ヤンクバッファに蓄えられている文字列をペーストします。
INCAR ファイルにおいて、 「VOSKOWN = 1」の行から「ICHARG = 2」の行までを削除したいとしましょう。
# SCF input for VASP
# Note that VASP uses the FIRST occurence of a keyword
SYSTEM = Untitled (VASP)
PREC = High
IBRION = -1
NSW = 0
ENCUT = 400
NELM = 40
NELMIN = 2
EDIFF = 1.0e-05
EDIFFG = -0.02
VOSKOWN = 1
NBLOCK = 1
ISPIN = 1
INIWAV = 1
ISTART = 0
ICHARG = 2
LWAVE = .FALSE.
LCHARG = .FALSE.
ISMEAR = 1
SIGMA = 0.2
IALGO = 48
LREAL = .FALSE.
パッと以下のような操作が候補に浮かびます。
1, 2 は例えば 100行以上削除する時にはまだるっこしいです。 3の範囲指定「v」を使う方法なんですが、カーソルを行頭に持って行ったり、行末に持って行ったりするのがやや面倒です。 まあ、「0」と「$」の2ストロークだけと言ってしまえばそれだけなんですが。 しかし手間を省くための労力を厭わぬ偉大な魔術師の卵 (既に偉大な魔術師である方は、当然既に知っているでしょう?) の方々には是非「V」を覚えて頂きたいと思います。 「v」の代わりに「V」を用いることで選択範囲は始点終点を含む行単位で指定されることになります。 すなわち、VOSKOWN の行まで移動して「V」、そのまま ICHARG の行までカーソルを移動して「d」 で目的の編集が完了します。
なお、「選択範囲を指定し始めた時には『v』でやってしまったけど、やっぱり『V』にしとけば良かった」という時でも、選択範囲をキャンセルする必要はありません。 選択範囲指定中に「V」を押せば選択範囲の端点はそのままで「行指向の範囲選択」に切り替わります。 逆に「通常の範囲選択」へは「v」で移行できます。
「v」や「V」で操作範囲を指定できることは既に紹介しました。 しかし場合によっては単純な「何処から何処まで」では不便なことがあります。
たとえば、INCAR ファイルにおいて、「= の左辺のみを削除したいが各行の = 以降は残したい」場合を想定して下さい。 一つのやり方としては、「8xj.j.j.」が挙げられるでしょう。 また、各行で範囲選択を用いて「v7ldj.j.j.」も悪くありません。 しかし、「C-v」を用いることでもっとスマートに、ビジュアルに編集できます。 PREC の行の頭にカーソルを持って行き、そこで「C-v」、続いて hjkl のカーソル移動、その状態で「d」で削除してみて下さい。 百聞は一見に如かず。 ここで私がグダグダ言うよりも、一度やってみる方が百倍分かり易いでしょう。
なおこのような領域指定の方法は、一般に「矩形選択」と呼ばれています。 Emacs や秀丸などの他のエディタでもしばしば実装されている機能です。 今時は必要なときにインターネット検索すれば済む事が多いので、「矩形選択」という名前を覚えることが一番大事だと思います。 しかし、vim の場合は選択範囲「v」のヴァリエーションということで、コマンドキーを覚え易いのではないでしょうか。
なお、以下はそれぞれ範囲選択中(選択カーソル移動中)に 自由に切り替えることができます。
たとえば「v」の範囲選択中に「あ、矩形選択にしておけば良かった」と思ったら、その場で「C-v」してやれば、始点と現在のカーソル位置を対角とする矩形選択に切り替わります。 その状態から元の範囲選択に戻したければ「v」ですし、行指向の範囲選択にしたければ「V」で切り替わります。 ……おっと、現在の範囲選択モードと同じキーを打つと、それは「範囲選択をキャンセル」になってしまいますよ。
追補: set virtualedit=block しておくと、「C-v」の矩形選択の際に行末位置より後方にカーソルを置けるようになります。
ここまでで述べたこと以外に、 以下のようなことができます。 (他の項と重複する物もありますが、ここにもまとめておきます。)
この他に以下のような操作があるらしいですが、私はあまり使ってません。 * r : 選択範囲を全て一種類の文字で埋める * テスト環境を構築する以外にはあまり用途が思い付きません * u : 小文字へ変換 * たとえば「PREC」などの識別子を「prec」と小文字に変換しなければならなければいけない時。(u は「UPPER CASE」の逆、という意味だろうか) * U : 大文字へ変換(UPPER CASE) * ~ : 大小文字逆転 * A : 範囲の直後に追記(矩形選択ならば各行の範囲の後) * c では一文字以上削除する必要があるけれど、削除せずに追加したい場合に便利かも。たとえば、「c」の例に対して「=!」で置換したい場合に便利かも。でも「c」で足りる場合が多い気がする * I : 矩形選択の場合、各行の範囲の先頭に挿入 * J : 範囲内を連結して1行にする
以上で説明したのと等価なキー操作は以下。打ち易い方だけ覚えれば良いと思います。 * x(dと同じ) : 削除 * X(dと同じ) : 削除 * s(cと同じ) : 削除して追記 * Y(yと同じ) : ヤンク * P(pと同じ) : 範囲を全部削除し、一度だけペースト
別のファイルに移る際に一度編集から抜けた扱いになって、以前の編集履歴は使用できなくなるようです。
テキスト形式で吐き出されるデータを整形しているときに、「部分的に外部コマンドを使いたい」と思うことはありませんか? 「sort したい」とか「3100行目を ‘grep Li’ したい」とか。 vi ではテキストを外部コマンドを使って編集する手段が用意されています。
:<範囲> ! コマンド
まず範囲指定し、それに続けて「!」でコマンドを実行することを宣言、その後にコマンドを記述します。 動作としては、指定された範囲をストリームとしてパイプでコマンドに渡し、コマンドが標準出力に返すストリームを受け取って指定した範囲を置き換えます。
:% !sort -n +1
原子が感じるポテンシャル(mkavepot.rb の出力)を欠陥からの距離でソートするのに役に立ちました。 (-n は数値順ソート、+1 は1番目の要素(左端の要素を0番目と数える)でソート)
:'<,'>!grep Li
不要な行を削除するのにも使えそうです。
*/INCAR
の ENCUT を片っ端から「400→500」に修正する例複数のファイルを一気に修正して回りたい時は、 コマンドラインから「vi */INCAR
」のように呼びます。 こうすると複数のファイルを「順番に」編集できるようになります。 「同時に」ではありません。 各々は別個のファイルなんですから。 1つのファイルの編集を終えて保存したら、 以下のコマンドで次に編集すべきファイルに移動できます。
次のファイルに移る前に「:w」で保存しとかないと警告出して止められます。 「:w」「:n」 は「:wn」と一回で指定することもできます。
さて、こうやって連続して編集すると何がオイシイかと言うと、そのセッションの間は「検索文字」と「最後の編集」を覚えていてくれることです。 最初のファイルについて「/400」「r5」「:wn」してやったら、次のファイルは「n.:wn」で作業が終わります。 あとは「n.:wn」「n.:wn」……と最後のファイルが終わるまで繰り返せば良いだけです。
この手の「複数のファイルに対する編集作業」は、Emacs よりも断然 vi の方が速いです。 とゆーことで、vi で最も覚えるべきは以下の2つです。
これによってカーソルを目で追わなくても「移動→編集」をガシガシ行えます。 これ以外の編集作業は Emacs など他のエディタでやってもそんなに不便ではない(or そっちの方が便利)だったりします。 まあ起動が軽いので単発の編集でもよく使いますが。
(:N については おまぬけ活動日誌 さんに教えて頂きました。 ありがとうございました。)
一文字削除して挿入「s」は既に紹介したのですが、挿入される文字が1文字であるならば、「r」を使うと最後に入力モードから抜けるために[ESC」を叩く手間が省けます。
たとえば、以下のファイルがあるとします。 (ちなみにこれは VASP の POSCARの一部分で、原子の配置を表しています。)
0.000000000 0.000000000 0.499990000
0.333333333 0.666666666 0.000000000
0.666666666 0.333333333 0.000000000
0.000000000 0.000000000 0.000000000
1行目の 0.499990000 を 0.500000000 に書換えようとして、 「4」の場所にカーソルを合わせたとします。 ここで「cw500000000」で修正してもいいでしょうし、「5s50000」でもいいでしょう。 しかしいずれにせよタイプの数を数えるのが面倒です。 フォーマットを崩したくない場合は連続上書きモード「R」が便利です。 1文字上書き修正の「r」の強力版と考えれば覚え易いと思います。 このファイルの場合、「R50000」で望む結果が得られます。 連続上書きモードで編集すると、例えば「R50000000」でも問題ないのでタイプの回数を細かく気にしなくてもいいので少しラクだと思います。 あと私は、系の対称性を崩すために原子の配置を少しずらす時にもよく使ってます。
まとめると、この手のデータファイルで並びを崩さずに編集したいときに便利ですよー、ということで。
英文を編集していたりすると、「この文字は Capital にしなきゃいけなかった」とかいう場面は頻繁に発生します。 また、定数(大文字ワード)や変数(小文字ワード)、CamelCase を扱っていると、やはり大小文字を書き変えることは少なくありません。 そういう時には「~」での「大小文字変換」が便利です。
たとえば、POSCAR ファイルの 「.FALSE.」を「.false.」にしたければ、「F」の位置で「~….」で用が足せます。
この目的には、範囲選択も利用できます。 「v」「V」「C-v」などで範囲を指定した状態で「~」を打てば、その範囲内の全てのアルファベットの大小が入れ変わります。 (ちなみに、全部大文字にするには「U」、小文字にするには「u」でいけます)
「CamelCase」という文字列に対して、以下の操作を行えば、
less でテキストを眺めていて「あ、ここ修正したいな」みたいな場合、less上で「v」を押してやると vi を起動して編集できます。 vi を抜けると less は新しいファイルをリロードし、表示に反映します。
いやまあ環境変数 EDITOR に emacs を指定しておけば Emacs が起動するんですけどね。 でも Emacs は起動が重いので「ちょこっと修正」なら vi の方が便利かと思います。
入力モードで [C-p] すると、今打ちかけてる文字列と一致する単語を探して補完してくれます。 例えば、以下のようなファイルがあった場合、
nominal 名目上の
percolation 透過
perturb 動揺させる
precursor 先駆者、先駆物質
predominantly 大部分は
regime 領域
relevant 関連性のある(to)
residual 余りの
retrieve 救出する、回復する、検索する
scatter 散乱する
semantics 記号論、記号の意味
shallow 浅い
「i pre[C-p]」と打ってやると、precursor → predominantly と補完候補を探ってくれます。 逆方向に補完候補を戻るには [C-n] です。 C/C++ プログラミングでは、インクルードしているヘッダファイルを走査してそこで定義されている識別子からも補完してくれます。 C/C++ に限らず、プログラミングの場面で非常に強力です。
あちこちで入力できる!
……面白いんだけれど、普通にコマンドモードに抜けた方がアンドゥとかの操作がシンプルになって良いと思います。
「:w」や「:%s/old/new/」の置換などの「:」で始まるコマンドは履歴が残ります。 「:」でコマンド入力の状態にしてから C-p で遡ることができます。 この履歴は、一度 vim を閉じても残ります。
私は「:%s/old/new/」のようなファイル全体を置換するコマンドを呼び出すのによく使います。
「:r file」 で file という名前のファイルの内容をカーソル位置にペーストできます。 別ウィンドウを開かなくてもペースト操作できるので、慣れれば結構スマートな感じがします。
「:r」は「:read」の短縮形で、勿論「:read file」でも同様に動作します。 なお、「:w」は「:write」の、「:q」は「:quit」の短縮形です。
UNIX コマンドを実行した結果をペーストすることもできます。 岸田がよく使うのは以下のコマンドです。
スクリプトに記述するファイル名などは 間違っていたら不要なデバグ時間がかかってしまうため、 私は殆どの場合手打ちではなく「:r! ls foo.dat」のように入力しています。
「:r」の部分は先の「:read」と同じ意味です。 これに続く「!」はコマンドの実行を意味し、「:r!」で「コマンド実行した結果を読み込む」という意味になります。
コマンド実行部分はシェルが受け持つので、パイプや for 文なども使うことができます。 私がよく使う、これの便利な用法があります。 データファイルなどで数字を1から順に書きたい場合があります。 そういうとき私は(普段 zsh を使っているので)
:r! for i in {001..100}; do ; echo $i; done
を使って、
001
002
003
:
100
という100行の文字列を得ています。 zsh を使ってない人は、以下でだいたいOKでしょう。
:r! yes * | cat -n | head -n 100
こうやって得た結果を「100J」で1行にまとめたり、 「C-v」の矩形選択でカットして挿入したい列に「p」でつっこんだりします。
「:e newfile」で現在編集しているファイルを離れて、newfile という名前の別ファイルの編集を新しく始めることができます。 (「:e」は「:edit」の短縮形)
現在のファイルの編集内容を保存せずに破棄して移るには「:e! newfile」です。
ファイル名を省略して「:e!」にすると最後にセーブした状態に復帰することができます。
「色々編集してみたけれどなんかしっくり来ないので編集結果を破棄したい」という場合、 勿論「:q!」で強制的に終了してもう一度開いたり、(vim ならば無制限にアンドゥができるので)「u」を押しまくっても良いのですが、「:e!」なら一手間でできます。
vi(vim)については既に書きましたが、今回は「オイシイ初期設定ファイルの作り方」です。
岸田が現在使っている ~/.vimrc を置いておきます。
「“」以降がコメントになります。 「何ができるか」はコメント見れば大体分かりそうですね。
なお、map, imap 命令は以下の2つの方法で記述できます。
コピペとか編集性のためには
タブ文字は確かに便利なんですが、どのような状況でも最適なタブ幅というのが決定できないのが問題です。 プログラムのインデント幅としては 2〜4文字が良いのですが、テーブルを組む場合には8文字でも足りないことがあります。
「*.c
のファイルにはタブは2文字」、 「*.dat
では8文字」という場合分けをアプリケーションにさせることもできますが、 こういうことさせると途端に煩雑になります。 というのも、そのテキストファイルを扱うのはエディタだけではないからです。 less や cat、grep などでもエディタでされるように表示されないと不便なこともしばしばです。 仮にテキストファイルを10種類に分類し、テキストファイルを扱うソフトウェアが10個あった場合には、 100通りの組み合わせの設定をしなければならなくなります。
そういうわけで、私は特に理由がなければタブ文字を使用しないようにしています。 vim においては以下のような設定をしています。
set expandtab " タブ文字の代わりに同じ幅の空白文字を入れる
set tabstop=2 " タブ文字の表示幅
オートインデントは便利なんですが、マウスの中クリックでのペーストしたテキストがどんどん右にずれていくことがあります。 私は頻繁にこの操作を行うのでオートインデントはオフにしています。
「なんか良い解決法ないかなあ」と思っているのですが。
"set autoindent "ENTER 時に自動でインデント
"set smartindent "賢い自動インデント
set incsearch " サーチをインクリメンタルサーチにする
好みが分かれそうです。 通常のサーチでは /string[ENTER] と [ENTER] を打った所で実行するので、次の動作に滞りなく入れるのですが、 インクリメンタルサーチだと /string で「string」という文字列を打ち込んでいる最中もサーチが進みます。 この時によく [ENTER] を打ち忘れて hjkl でカーソル移動しようとして失敗してしまいます。
ですが、理性で考えると [ENTER] を入れる手間さえ忘れなければ、 キー入力の総数は必ず通常サーチ以下になるので「こちらで慣れた方が便利な筈だ」、 と自分に言い聞かせてインクリメンタルサーチを使っています。
set hlsearch " 検索結果文字列のハイライト
なお、vim での編集中にハイライトがうざくなった場合は、
:set nohlsearch
のコマンド入力でハイライト表示をやめることができます。 私は以下を ~/.vimrc に仕込んで C-k で実行できるようにしています。
map <C-k> :nohlsearch<CR>
デフォルトではバックスペースには様々な制限が加わっています。 backspace を設定することでそれを制御することができます。
set backspace=indent,eol,start "eol:改行,start:入力モードに入る前の文字
この設定は、「行頭の空白」「改行文字」「入力モードに入る前の文字」の削除を許可します。
vim には3種類のモードがあり、この各モードによってキーマップを設定するコマンドが異なります。
imap <C-a> <Esc>:w<CR>a
" ↑保存して入力モードに復帰
私はテキストを一行打つ間にも何度か保存するのが癖になっています。 「[ESC]:w[ENTER]a」 の作業が面倒なので、一つのキーバインドにまとめて快適に保存しています。
以前はこの動作に [C-w] を当てていたのですが、これだとウィンドウ操作とバッティングしてしまうため [C-a] に当てることにしました。 (本当は [C-s] に当てたいところですが、 通常の端末では端末の停止と解釈されてしまうためこれをやりたければ 端末の設定を変更する必要があります。)
map <C-u> <Esc>
" ↑C-uを検出すると入力モードを抜ける
[C-u] を打つと入力モードに入ってから打ち込んだ全ての文字が消えてしまいます。 こういう操作が欲しい場面もあるかもしれませんが、タイプミスで[C-u]を押してしまって一生懸命打ち込んだテキストがパーになることが私はよくあります。 ということで[C-u]にその元々の意味を失わせるために設定を上書きしています。
imap <C-d> <Esc>:q<CR>
[C-d]の1操作で編集を終了します。 端末の終了も C-d で行うようにしておけば、 C-d 連打で終われるので楽に一日の作業を終えることができます。
if has("gui_win32") " Windows 環境ならば
if filereadable( $HOME . "/.vimrc.windows" )
source ~/.vimrc.windows
endif
else
if filereadable( $HOME . "/.vim/colors/ippei.vim" )
colorscheme ippei
endif
endif
環境に差がある部分だけを場合分けしています。
なお、Windows の .vimrc.windows にはフォント設定を入れています。
set guifont=MS_ゴシック:h9:cSHIFTJIS
これは文字コードの関係で(SJISで書かなければならない)別ファイルにしています。
これまで紹介したのよりは覚える必要性は低いかな、という項目を箇条書します。
vi 本をさらうと知らないコマンドが幾つも出てきました。 まだ結構あるみたいですが、もう十分だと思いました。 もうおなかいっぱいです。