Vimスクリプト基礎文法最速マスター

流行ってるみたいなので遅ればせながら便乗。需要?何それおいしいの?

以下、Vim 7.2 を前提とします。

1. 基礎

全てコマンド

Vimスクリプトはコマンドの羅列です。単純な代入や関数の呼び出し、if などの制御構文も全てコマンドです。

echo コマンド
echo 'Hello, world!'

:echo コマンドの出力には必ず改行が含まれます。改行を含みたくない場合は :echon コマンドを使います。

echon 'Hello, '
echon 'world!'
コメント

" から行末までが行コメントです。

" コメント

Vimのキモい制約により、コマンドによってはコメントが使えない場合があります*1。

代入

値の代入には :let コマンドを使います。

let a = 1
データの型

データには型があり、以下のいずれかです。

  • 整数値
  • 小数値
  • 文字列
  • リスト
  • 辞書
  • 関数参照

関数参照はこの記事では省略します。

実行

実行するには :source コマンドを使います。

source script.vim

シェルから実行する場合は -S オプションを使います。

vim -S script.vim

2. 数値

数値の表現

数値には整数と小数があります。小数は Vim 7.2 より導入されました。

let num = 1
let num = 1.234
let num = 12.0e-3
四則演算

四則演算です。

let num = 1 + 1
let num = 1 - 1
let num = 1 * 2
let num = 1 / 2

+ と - のみ複合演算子も使えます。

let num += 1
let num -= 1

*= や /= は使えないので注意してください。

剰余もあります。

let num = 3 % 2

Vim の小数は後付けのため整数同士を割り算しても結果は整数のままです。

let num = 3 / 2   " => 1
let num = 3 / 2.0 " => 1.5

3. 文字列

文字列の表現

文字列はシングルクォートかダブルクォートで囲みます。
ダブルクォートの中では\t(タブ)や\n(改行)などの特殊文字を利用することができます。
シングルクォート内ではシングルクォート自身を表現するのにシングルクォートを 2 つ重ねます。それ以外の文字は一切展開されません。

let str = "abc"
let str = 'def'
let str = "abc\ndef"
let str = 'That''s right!'

文字列操作

各種文字列操作です。

" 結合
let join1 = 'aaa' . 'bbb'
let join2 = join(['aaa', 'bbb', 'ccc'], ',')

" 分割
let record = split('aaa,bbb,ccc', ',')

" 長さ
let length = strlen('abcdef')

" 切り出し
let substr = 'abcd'[1]      " b
let substr = 'abcd'[0 : 1]  " ab
let substr = 'abcd'[ : 1]   " ab
let substr = 'abcd'[2 : ]   " cd
let substr = 'abcd'[1 : -1] " bcd

" 検索
let result = stridx('abcd', 'cd')  " 見つかった場合はその位置、見つからなかった場合は-1が返る

4. リスト

リストの表現
let list = [1, 2, 3]
リストの要素の参照と代入
let list = [1, 2, 3]
let e0 = list[0]       " => 1
let e2 = list[-1]      " => 3
let e1_2 = list[1 : 2] " => [2, 3]
let e_1 = list[ : 1]   " => [1, 2]

let list[0] = 10
let list[1] = 20
" => list == [10, 20, 3]
リスト操作

リストを操作する関数です。

let list = [1, 2, 3]

" len(リストの長さ)
let length = len(list)  " 3

" remove(任意の要素を取り出す)
let first = remove(list, 0)  " first == 1, list = [2, 3]
let last = remove(list, -1)  " last == 3, list = [2]

" insert(任意の位置に要素を追加)
" (単純な関数の呼び出しには :call コマンドを使います。)
call insert(list, 5, 0)  " list == [5, 2]

" add(末尾に要素を追加)
call add(list, 9)  " list == [5, 2, 9]

5. 辞書

辞書は、他の言語でハッシュテーブルや連想配列と呼ばれているデータ構造です。キーには文字列のみ使用できます。

辞書の表現
let dict = {}  " 空の辞書
let dict = {'a': 1, 'b': 2}
辞書の要素の参照と代入
let a = dict['a']
let b = dict.b

let dict['a'] = 10
let dict.c = 30
辞書操作
" 全てのキーをリストで取得
let keylist = keys(dict)

" 全ての値をリストで取得
let valuelist = values(dict)

" 全てのキーと値を [[key, value], ...] の形式のリストで取得
let itemlist = items(dict)

" キーの存在確認
let exists = has_key(dict, 'a')

" キーの削除
call remove(dict, 'a')

6. 制御コマンド

if コマンド
if 条件
  
endif
if 〜 elseif コマンド

他の言語のように else if ではなく elseif であることに注意しましょう。

if 条件
  
elseif 条件
  
endif
while コマンド
let i = 0
while i < 5
  " 処理
  
  let i += 1
endwhile
for コマンド

for はリストの中身を走査するコマンドです。

for e in [1, 2, 3]
  echo e
endfor

7. その他の演算子

ここまでで出てきていない演算子です。

比較演算子
  • ==
  • !=
  • >
  • >=
  • <
  • <=

これらの意味は一般的な言語と同様です。数値同士の場合は数値比較、文字列同士の場合は文字列比較になります。
文字列と数値を比較した場合、文字列が数値に変換され、数値として比較されます。
文字列同士を比較する場合、大文字と小文字を区別するかは Vim の 'ignorecase' オプションに左右されます。このオプションを無視して比較するには以下の演算子を使います。

  • 大文字小文字を区別する
    • ==#
    • !=#
    • >#
    • >=#
    • <#
    • <=#
  • 大文字小文字を無視する
    • ==?
    • !=?
    • >?
    • >=?
    • <?
    • <=?

正規表現マッチをするには以下の演算子を使います。

比較演算子のまとめです。

'ignorecase'次第 大小文字考慮 大小文字無視
等しい == ==# ==?
等しくない != !=# !=?
より大きい > ># >?
より大きいか等しい >= >=# >=?
より小さい < <# <?
より小さいか等しい <= <=# <=?
正規表現マッチ =~ =~# =~?
正規表現非マッチ !~ !~# !~?

== と != はリストや辞書の同値性テストに使えます。

let res = [1, 2, 3] == [1, 2, 3]  " 1

is と isnot 演算子は、数値や文字列の場合は型変換を行わない厳格な比較をします。リストや辞書の場合は同一性テストをします。

let res = 1 is '1'  " 0
let res = 1 is 1    " 1

let list = [0, 1]
let res = list is [0, 1]  " 0
let res = list is list    " 1
論理演算子

俗に言う短絡演算子、ショートサーキット演算子です。結果は真偽値を表す 0 か 1 に変換されます。

let a = 10 || 0  " a == 1
let a = 10 && 5  " a == 1
let a = 10 && 0  " a == 0
条件演算子

一般的には三項演算子とも呼ばれています。

echo cond ? 'true' : 'false'

8. 関数

関数は :function コマンドで定義します。ユーザ定義の関数名は組み込み関数名と区別するために大文字で始める必要があります。

function! Sum(v1, v2)
  return a:v1 + a:v2
endfunction
  • function! の ! は、同名の関数を再定義する際に既存の関数を上書きすることを示します。スクリプトを再読み込みした時のために、基本的に付けておいた方が良いでしょう。
  • 関数内で引数にアクセスするには、a: という接頭子が必要です。

同一スクリプト内でのみ使える関数(スクリプトローカルな関数)を定義するには、関数名の先頭に s: を付けます。この場合 s: 以降の関数名は大文字で始める必要はありません。

function! s:sum(v1, v2)
  return a:v1 + a:v2
endfunction

可変長引数を使いたい場合は引数の最後を ... にします。可変長引数にアクセスするには以下のようにします。

a:0
可変長部分の引数の数
a:n
n は数字。n 番目の引数にアクセスする。a:1 a:2 など。
a:000
可変長部分を表すリスト。ぶっちゃけこれを使えば前者の 2 つはなくてもなんとかなる。
function! s:sum(v1, ...)
  let num = a:v1
  for i in a:000
    let num += i
  endfor
  return num
endfunction

9. ファイル入出力

Vim のバッファを経由する方法もありますが、ここでは Vim の環境に影響を与えない方法を示します。

readfile() 関数

ファイルの中身を行区切りのリストで取得します。

for line in readfile(fname)
  if line =~ 'Date'
    echo line
  endif
endfor
writefile() 関数

行区切りのリストをファイルに書き出します。

call writefile(['line1', 'line2', 'line3'], fname)

知っておいた方がよい文法

真偽値

:if :while や条件演算子等の真偽値が求められる場所では、値は数値に変換され、その値が 0 かそれ以外かで判定されます。0 の時は偽、それ以外の時は真です。

値の変換

先ほどの真偽値や、文字列の連結、数値演算など特定の箇所では値は暗黙的に適切な値に変換されます。
整数値が文字列になる場合は、10 進数表記の文字列になります。
文字列が整数値になる場合は、リテラル表記と同様に解釈されます。文字列の末尾にゴミがあっても無視されます。変換できない場合は 0 に変換されます。
それ以外の暗黙な変換は行われません。整数値と小数値の変換も暗黙的には行われないので注意してください。
変換が必要な場面で型の変換ができない場合はエラーになります。

値の再代入

すでにある変数に値を代入する場合、元の値と新しい値が型変換可能な型でない場合はVim は融通が利かないのでエラーになります。
その場合は :unlet コマンドを使って一旦変数を削除する必要があります。

let a = [1,2,3]
"let a = 1  " エラー!
unlet a  " 一旦 unlet する必要がある
let a = 1

これは特に様々な値が入ったリストを :for で回す際に注意が必要です。

for i in [0, 1.1, ['list'], 'string']
  echo i
  unlet i  " i は for の部分で再代入されるので unlet が必要
endfor
変数が定義されているかどうかの判定

exists() 関数を使います。

let e = exists('var1')
便利な関数
  • range()
    • 連番のリストを生成します。:for と組み合わせると効果的です。
    • range(3) == [0, 1, 2]
    • range(1, 3) == [1, 2, 3]
    • range(1, 5, 2) == [1, 3, 5]
  • map(expr, string)
    • 第一引数のリストの各要素を string で渡した文字列を評価した値に変換します。v:val が要素として参照できます。
    • 辞書も渡せます。辞書の場合は辞書の各値が変換される他、v:key でキーを参照できます。
    • 渡したリスト自体が書き換えられるので注意してください。コピーは作られません。
    • map([1, 2, 3], 'v:val + 1') == [2, 3, 4]
  • filter(expr, string)
    • 第一引数のリストの各要素を string で渡した文字列を評価した結果が真になるもののみになるようにフィルタリングします。v:val が要素として参照できます。
    • 辞書も渡せます。辞書の場合は辞書の各値が評価される他、v:key でキーを参照できます。
    • 渡したリスト自体が書き換えられるので注意してください。コピーは作られません。
    • filter([1, 2, 3], 'v:val % 2 == 1') == [1, 3]
リスト代入

リストを複数の変数にばらして代入できます。代入先の変数の数と代入元のリストの長さは同じである必要があります。

let [key, value] = ['key', 'value']
let [e1, e2; rest] = ['e1', 'e2', 'rest1', 'rest2']
例外処理

専用のコマンド群があります。

try
  throw "エラーメッセージ"
catch /error/  " エラーメッセージにマッチする正規表現
catch          " 空の場合は必ずマッチする
  " ... 例外処理 ...
finally
  " 必ず行われる処理
endtry

参考資料

全て :help に書いてあります。Web 上の怪しい資料*2には誤ったものや古いものが多いので、可能な限り :help を参照しましょう。



Vimスクリプトをプログラミング言語の 1 種として見た場合の使い方について、網羅的とは言えませんが簡単にまとめて見ました。ご存知の通り Vimスクリプトは Vim を拡張するための言語なので、Vim の状態にアクセス、操作する方法を知らないと実はあまり役に立ちません。
ただ、そこまで書いてるとひどいボリュームになりそうだったので今回は省略しました。

*1::map系や:!など

*2:この資料も含む