Vimコマンド&Script入門

コマンド&スクリプト

VimとVimScriptの入門記事です。
Vimはヘルプが充実しているので、最終的にはヘルプに頼ればいいのですが、どうやったら知りたいヘルプを見られるかってのも問題の一つかなと思ってます。

入門者の助けになるように、不定期で更新していますが、てこずってます。

Contents

スポンサーリンク

基本

VimScriptを使うには、やはりVimの起動が必要です。
ターミナルから、vimコマンドで起動します。

takk@makku ~ % vim

:echo (:ec) 表示

:echo 値

Hello Worldにあたるであろう、echoから使ってみます。
:(コロン)を入力後、続けてecho “HELLO”と入力します。

:echo "HELLO"


Enter押すまでは実行しません。


Enterを押すと、このように左下にHELLOが表示されます。

echoは数式を与えると計算結果を返します。

:echo 30+40

Enter実行。

ヘルプの表示方法

:h ec

関連記事はコチラ
Vimスクリプト(echo)

変数

変数の型

変数は以下の型に分類されます。
Number 32bitまたは64bitの符号つき整数 e.g. -10 0 20
Float 浮動小数点数 e.g. -12.345
String 文字列 e.g. “HELLO”
Funcref 関数参照 e.g. function(“strlen”)
List リスト e.g. [1,2,3,[var1,var2,”string”,4]]
Dictionary 辞書 e.g. {“apple”:100, “banana”:200}

ヘルプの表示方法

:h varia

:let (:let) 変数代入

:let 変数 = 値

letで変数に代入ができます。

aには12が格納されていますので、12 * 12の計算です。

echoを実行すると計算結果が表示されます。

ヘルプの表示方法

:h let

:for (:for) 繰り返し

:for 変数 in リスト
:endfor

forで繰り返し制御できます。

:let arr=[]
:for i in range(1,10)
:  call add(arr, i)
:  endfor
:echo arr

ヘルプの表示方法

:h for

function 関数

:function (:fu) 定義関数の一覧表示

:function

:fu

:function (:fu) 関数定義

:function 関数名()

関数名はアルファベット大文字から始めなければなりません。

:fu Test()
: echo "HELLO"
:endf
:echo Test()

ヘルプの表示方法

:h fu

:delfunction (:delf) 定義関数の削除

:delfunction 関数名

:delf Test

ヘルプの表示方法

:h delf

連想配列

連想配列使います。辞書ともいいますね。

連想配列の定義

:let dict={"AA":10, "BB":20, "CC":30}

連想配列の使用

定義するときは、{}を使いましたが、使用するときは配列と同じです。

:echo dict["BB"]


または、.(ドット)を使ってもアクセスできます。

:echo dict.BB


.(ドット)を使う方式だと”も[]も入力しなくて済むので4文字楽できますね。

連想配列の初期化

連想配列をクリアするには、要素のない初期化をしてやればよいです。

:let dict={}
:echo dict

Blob Binary large Object

初期化

:let data = 0z3132

参照

:echo data[0] data[1]

関連記事はコチラ
Vimスクリプト(blob)

setline バッファへ文字列書き込み

setline(行指定,テキスト)

バッファの指定行へ文字列を書き込みます。

:call setline(".","HELLO")

setlineは実行すると現在行に指定した文字列を書き込みます。

新規のバッファの1行目に文字列を書き込みました。
続けて3行目を指定して、setlineを実行してみます。

存在する行か、存在する行の次の行、の指定でかければsetlineを実行しても失敗します。

存在する行の次の行、つまり2行目を指定してもう一度setlineを実効してみます。

今度は実行できました。

すでに文字列が書き込まれている行を指定すると、

このように上書きされます。

line 行番号を取得

line(行指定 [,windid])

行番号を得ます。

カーソル行が3行目の状態で実行すると、

:echo line(".")


3が返ってきました。

バッファ末”$”を指定すると、

:echo line("$")


バッファ末尾行の行番号が返ってきました。

10行空行を追加します。

:norm 10o

getline バッファから文字列取得

getline(行番号)
getline(行番号開始,行番号終了)

3行目の文字列取得

:echo getline("3")

複数行の取得は、開始行と終了行の二箇所を指定すればよいです。
2行目から4行目のテキストを取得。

:echo getline("2","4")


戻り値はリストで返ります。

getlineの戻り値を変数に格納するには、letを使って代入します。

:let aaa=getline("2","4")

リストの添え字は0始まりです。

:echo aaa[1]

append バッファへ追記

append(行番号, 文字列、または文字列のリスト)

setlineは、指定した行を書き換えてしまいますが、appendを使えば、行の挿入ができます。

:call append("3",["111","222"])

execute :コマンド実行

execute(コマンド)

:から始まるExコマンドを実行します。

/tmp $ touch test_{1..5}.txt
/tmp $ vim test_*.txt

extecuteの結果をechoで表示

:echo execute("args")

getline setline応用

テキストの上下反転

3つの関数を組み合わせて、テキストを上下反転してみます。

getlineでテキストを取得し、reverseで反転し、setlineでテキストに書き込みます。

テキストの左右反転

前回はテキスト上下反転を行いましたが、今回は左右反転です。
土台のテキストを作成。

:r0!wsl seq 100 | wsl pr -t10J

このテキストを変数に格納します。

:let a=getline("w0","w$")
:echo a

3行目を1文字毎に区切り、別変数に格納します。

:let b=split(a[2],'\zs')
:echo b

別変数に格納した文字のリストを反転、結合し、一行の文字列にします。¥

:let a[2]=join(reverse(b),"")
:echo a[2]

最後に、テキストを入れ替えます。

:call setline("w0w$",a)

文字列操作

.(ピリオド) 文字列連結

文字列 . 文字列

文字列どうしを、.(ピリオド)で連結できます。

:let arr=getline("1","$")
:echo arr[2] . arr[0]

strlen 文字列長を得る

strlen(文字列)

:echo strlen(getline("4"))

eval 文字列を評価する

eval(文字列)

文字列を評価します。

:echo eval(getline("1"))


1行目の文字列を読み込んで、その文字列を評価した結果がechoされました。

join 文字列連結

join(リスト [,セパレータ])

リストで指定した文字列をセパレータで連結します。セパレータを省略すると空白で連結します。

バッファの全域を指定して連結。

:echo join(getline("1","$"))

str2float 文字列を数値に変換

str2float(文字列)

:let arr=getline("1","$")
:echo str2float(arr[2]) + str2float(arr[3])

split 文字列をセパレータで分割

split(文字列 [,パターン])

split関数を使います。

:echo split(getline("1"),",")

toupper 文字列を大文字へ

toupper(文字列)

:echo toupper(getline("1"))

tolower 文字列を小文字へ

tolower(文字列)

:echo tolower(getline("2"))

printf 書式付き文字列

printf(フォーマット, 表示項目…)

printfで書式付き文字列を作れます。

:let a = "HELLO"
:echo printf("[%10s]", a)

マイナスをつけると、左詰になります。

:let a = "HELLO"
:echo printf("[%-10s]", a)

文字数で切り詰め。

:let a = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
:echo printf("[%.5s]", a)

:let a=100
:echo printf("[%d]",a)

まあ普通に表示されますね。

計算した結果はどうでしょうか。

:let a=100
:echo printf("[%d]",a * a)

桁指定してみます。右詰。

:let a=100
:echo printf("[%10d]", a)

左詰。

:let a=100
:echo printf("[%-10d]", a)

16進数。

:let a=100
:echo printf("[0x%x %xH]", a, a)

8桁にしてみます。

:let a=100
:echo printf("[%8xH]", a)

アドレス表示風に0埋めの8桁にしてみます。 8の前に0をつけるだけです。

:let a=100
:echo printf("[%08xH]", a)

大文字で表示。

:let a=100
:echo printf("[%08XH]", a)

8進、2進の書式です。

:echo printf("%o %o %o %o %o", 5, 6, 7, 8, 9)

桁数指定。

:echo printf("[%03o]", 0644)

2進数。

:let a=9
:echo printf("[%04b]", a)

:let a=0xff5a
:echo printf("[%016b]", a)

floatです。

:let a=23.45
:echo printf("%3.5f", a)

変数を整数にすると、

:let a=23
:echo printf("%3.5f", a)

特に問題なく小数点入りで表示されますね。

整数部は省略できるか。

:let a=23.45
:echo printf("%.5f", a)

リスト

リストの定義

リストは[]を括り、各要素は,(カンマ)で区切ります。

:let a = [10,20,30]

リスト内の各要素を、ばらばらの変数に格納したい場合は、リストで受けることもできます。

:let [a1,a2,a3] = a
:echo a2

リストの範囲アクセス

:let a = [10,20,30,40,50]
:echo a[1:3]

add リストに追加

add(リスト,追加要素)

:let arr = [1,2]
:call add(arr,3)

len リストの要素数

len(リスト)

:echo len(getline("1","$"))

remove 要素の削除

remove(リスト, インデックス, [, end])

0始まりのインデックスを指定してリストの要素削除します。

:let arr=getline("1","5")
:call remove(arr,0)
:echo arr

reverse リストを逆順にする

reverse(リスト)

:echo reverse(getline("1","$"))

insert 要素の追加

insert(リスト,追加要素 [,インデックス])

リストに要素の追加をします。

:let arr=getline("1","$")
:call insert(arr,"111",1)
:echo arr

リストのコピー

変数名だけコピー

:let arr=getline("1","$")
:let arr2 = arr
:let arr2[1] = 111
:echo arr

copy リスト内もコピー

copy(リスト)

:let arr=getline("1","$")
:let arr2 = copy(arr)
:let arr2[1] = 111
:echo [arr,arr2]

deepcopy リストのリストもコピー

deepcopy(リスト)

copyはリスト中のリストの実体はコピーされません。

:let a=[1,[2,3]]
:let b=copy(a)
:let a[0]=100
:let a[1][0]=200
:echo a b

左がaで、右がbですが、a[1]とb[1]の値が同じになってますね。

リストのリストをコピーするには、deepcopyを使います。

:let a=[1,[2,3]]
:let b=deepcopy(a)
:let a[0]=100
:let a[1][0]=200
:echo a b

リストは再起的にコピーされるため、どれだけリストを内包していても、完全に別リストになります。

map

map(リスト,文字列)

mapで処理するリストの各要素は、v:val変数に格納されます。

extend

リストを拡張します。

リストの入れ子を平坦にする例。

:echo a

:let b=[]

:echo b

計算関数

or ビット演算

or(値,値)

2値をorします。

1 + 1は、(移行+は、論理和の意味)

:echo or(1,1)


1ですね。

1+2は、

:echo or(1,2)


3になりました。

31+3は、

:echo or(31,3)


31になりました。
31は、2進数で、

1111 1111

3は、

0000 0011

31と3を上下に並べると、

1111 1111
0000 0011

上段が全部1なので、論理和しても、全部1ですね。合ってます。

and ビット演算

and(値,値)

2値をANDします。

論理積1・1の計算をします。

:echo and(1,1)


当然1です。

次は、10・5

echo and(10,5)


0になりました。
10と5は、それぞれ2進数で表すと、

1010
0101

上下が1の列がないので、オール0で正解です。

:echo and(127,100)


100です。
2進数で確認してみます。

0111 1111
0110 0100

合ってますね。

invert NOTビット演算

invert(値)

NOTします。

50を2進数で表示すると、

:let a=50
:echo printf("%b",a)


110010ですね。

これをinvert()で実行すると、

:echo printf("%b",invert(a))


1がたくさん並んでいますが、右の方をみると、
001101になってます。

並べて確認してみます。

110010
001101

abs 絶対値

abs(式)

:echo abs(1.234)


1.234と表示されました。
そのままですね。

マイナス値を与えてみます。

:echo abs(-1.234)


マイナスが取れました。

変数で指定してみます。

:let a = -1.23456789


桁が多いので桁落ちして格納されたかもしれません。確認してみます。
-1.234568になってますね。
この変数を、absにかけてみます。

:echo abs(a)


マイナスが外れて、1.234568になりました。

acos

acos(式)

:echo acos(-1)


3.141593が表示されました。桁落ちしてるんですね。
-1.0を指定してみます。

:echo acos(-1.0)


同じですね。

次は、1を指定。

:echo acos(1)


0.0が返りました。

asin

asin(式)

:echo asin(0.0)


結果は0.0です。

:echo asin(0.5)


0.523599。

:echo asin(1.0)


1.570796が返りました。PI/2ですね。

:echo asin(-1.0)


-1.570796が返りました。

atan

atan(式)

:echo atan(0)


0.0が返りました。

:echo atan(0.5)


結果は0.463648。

:echo atan(1.0)


0.785398。

:echo atan(3.141592/2.0)


1.003885。

あれ1ちょうどじゃないんですね。atanってなんだったかな。

atan2

atan2(式)

:echo atan2(0)


エラーになりました。引数は一つじゃなくて、二つですね。

:echo atan2(0,0)


結果は0.0。

:echo atan2(-1,0)


結果は-1.570796。

:echo atan2(1,-1)

ヘルプの説明どおり、2.356194が返りました。

日付と時刻

localtime 現在日時取得

localtime()

:echo strftime("%c", localtime())

strftime 日付時刻書式に変換

strftime(フォーマット, [,日時])

:echo strftime("%c", 0)

getftime ファイルのタイムスタンプ取得

getftime(ファイル名)

/tmp $ touch test1.txt
/tmp $ vim -c 'echo strftime("%c",getftime("test1.txt"))'

バッファ関連

:ls バッファ一覧表示

バッファの一覧を表示します。

:ls


一番左の数字はバッファ番号です。
バッファ名=ファイル名です。
%がついているのがカレントバッファです。

:b (buffer) バッファの切り替え

:数字b :b数字 バッファ番号指定

:bufferコマンドで、バッファ切り替えできます。省略して:bが使えます。
例えば2番目のバッファに切り替えるには、

:b2


番号の指定は、bの前でも動作は同じです。

:4b


:(コロン)の入力を省略したい場合は、
数字を押してから(Ctrlを押しながら^)を入力することで、バッファ切り替えできます。

2<C-^>

:bfirst (:bf) :blast (:bl) 先頭、末尾バッファ

arglistの先頭のファイルを選択するのに:firstを使いましたが、buffer-listの先頭を選択するには、:bfirstを使います。bが先頭につくだけですね。ですので最後のファイルを選択するコマンドは:blastになります。
:bfirst :blast使ってみます。

:ls


カレントバッファは、buffer-listの最初にあります。カレントバッファを最後のバッファにしてみます。

:bl


次は最初のバッファ。

:bf

:badd (:bad) ファイルをバッファへ追加

指定した1ファイルを読み込んでバッファへ追加します。

:bdelete (:bd) バッファリストからバッファ削除

:bd番号で、指定した番号のバッファを削除します。

:bd1

1番のバッファが削除され、カレントバッファが2番のバッファになります。

:bwipeout (:bw) バッファ削除

:bdeleteはバッファリストから削除するコマンドでしたが、:bwipeoutは、バッファ自体も削除するコマンドです。

:bufdo (:bufdo) 各バッファで実行

buffer-listの中の全てのバッファへの編集だけでなく、rangeを指定することで、特定のバッファのみに絞り込んで編集ができます。
使ってみます。

/tmp $ for i in {1..5};do echo HELLO$i>test_$i.txt;done
/tmp $ vim test_*.txt

上記のように起動した後、lsを確認。

:ls

:ballで全てのバッファをスプリット表示しておきます。

:ball

:bufdoを使って、”HELLO”を”HELL “に置換してみます。

:bufdo! s/HELLO/HELL /


全バッファ置換されました。

bufdoで置換を実施しただけなので、各バッファはファイルへの保存はしてない状態のはずです。
:bmodifiedで編集中のファイルへ切り替わるか確認してみます。

:bmodified


一番上の窓のバッファがtest_1.txtに変更されました。編集中のバッファのうち先頭のバッファ、つまりtest_1.txtに
切り替わったってことですね。

もう一度、実行してみます。

:bmodified!

一番上の窓が、test_2.txtに切り替わりました。
全ファイルが保存されていない状態です。一旦終了して再起動してみます。

:qa!
/tmp $ vim test_*.txt

ballします。

:ball

次は範囲を指定して:bufdoを使ってみます。

まず複数ファイル作成してVim起動。

/tmp $ for i in {1..5};do echo HELLO$i>test_$i.txt;done
/tmp $ vim test_*.txt

buffer-listを確認。

:ls

2と3のバッファを置換してみます。

:2,3bufdo! s/HELLO/HELL /

全バッファ見てみます。

:ball


2と3のバッファが置換されてますね。

:bmodified (:bm) 変更バッファをカレントにする

変更したバッファをカレントバッファにします。
以下のように各バッファで画面スプリットしておき、

1番のバッファのみ修正します。

3番目のウィンドウに移動して、:bmを実行すると、

3番目のウィンドウが、1番のバッファになります。

bufname バッファ名取得

現在のバッファ名を返します。

bufname(“”) 現在のバッファを指定

:ls | echo bufname("")

bufname(番号) バッファ番号で指定

:ls | echo bufname(2)

bufexists バッファの存在チェック

指定したバッファが存在するかチェックします。画面に表示されていなくても存在していればTRUE(1)が返ります。

:ls | echo bufexists("sample3.txt")

sample3.txtというバッファは存在しないので、FALSE(0)が返りました。

getbufinfo バッファ情報を取得

バッファの情報を得る関数 getbufinfoを使ってみます。

/tmp $ vim -o file*.txt

:echo getbufinfo(1)

連想配列になってますね。

ヘルプを見ると、キーはこれだけあるようです。

bufnr           buffer number.
changed         TRUE if the buffer is modified.
changedtick     number of changes made to the buffer.
hidden          TRUE if the buffer is hidden.
listed          TRUE if the buffer is listed.
lnum            current line number in buffer.
loaded          TRUE if the buffer is loaded.
name            full path to the file in the buffer.
signs           list of signs placed in the buffer.
                Each list item is a dictionary with
                the following fields:
                    id    sign identifier
                    lnum  line number
                    name  sign name
variables       a reference to the dictionary with
                buffer-local variables.
windows         list of |window-ID|s that display this
                buffer
popups          list of popup |window-ID|s that
                display this buffer

nameだけ取り出してみましょう。

:echo a[0].name

複数のバッファの情報が取り出せるようにリストで返ってくるんですね。

ウィンドウ関連

バッファ(buffer)は、ファイルのテキストのこと(in-memory)
そして、ウィンドウ(window)は、バッファを覗くための窓。

windowは、一つのバッファに幾つあっても良いです。

buffer/file
 +--------+
 |        |
+----------+
||        || window
+----------+
 |        |
 |        |
 |        |
+----------+
||        || window
+----------+
 |        |
 |        |
 +--------+

バッファとファイルの関係は、一対一です。

:windo 各ウィンドウで実行

/tmp $ for i in {1..5};do echo HELLO$i>test_$i.txt;done
/tmp $ vim test_*.txt

起動直後はtest_1.txtのみが見えています。

test_3.txtのウィンドウも開きます。

:vs test_3.txt

各ウィンドウ(test_1.txtとtest_3.txt)内の文字列を置換。

:windo! s/HELLO/ABCD /

全バッファ見てみます。

:vert ball

VimScript win_getid

win_getidを使います。ヘルプから。

:h win_getid
win_getid([{win} [, {tab}]])                            *win_getid()*
                Get the |window-ID| for the specified window.
                When {win} is missing use the current window.
                With {win} this is the window number.  The top window has
                number 1.
                Without {tab} use the current tab, otherwise the tab with
                number {tab}.  The first tab has number one.
                Return zero if the window cannot be found.

引数は省略するとカレントウィンドウのIDを返してくるようです。

では使ってみます。何もファイルを指定せずVimを起動します。

/tmp $ vim

win_getidの戻り値をechoで表示。

:echo win_getid()

1000番が返ってきました。

スプリットした場合のウィンドウIDはどうなるでしょうか。

:vnew
:echo win_getid()

lで、スプリットの右側へカレントバッファを切り替えし、ウィンドウIDを確認。

:echo win_getid()


最初のバッファは1000番のままですね。

別バッファでも別ウィンドウIDが振られていることを理解しました。
バッファ別に振られるなら、別タブでも当然、別ウィンドウIDになるはずですが、一応確認してみます。

何もファイルを指定せずVimを起動します。

/tmp $ vim

ウィンドウIDを確認。

:echo win_getid()


1000番が割り振られています。

次に:tabnewして、win_getidします。

:tabnew
:echo win_getid()


1001番です。ウィンドウが増えるたびに増えていくのでしょう。
タブを削除(バッファを削除)した場合はどうなるでしょうか。
:bwipeoutしてから再度:tabnewしてみます。

:bwipeout
:tabnew
:echo win_getid()


1002番が割り振られました。
バッファ番号と同じように、同じ番号は割り振られないようですね。

複数バッファを開いたときに、それぞれのウィンドウIDがどのように割り振られるのか確認してみます。

/tmp $ rm test*
/tmp $ for i in {1..5};do echo HELLO$i>test_$i.txt;done
/tmp $ vim test_*.txt

ウィンドウは一つだけですね。ってことは、1000番が割り振られているはずです。

:echo win_getid()

1000番です。

次は、画面スプリットして別バッファを開いてみます。
test_3.txtを開きます。

:vs test_3.txt
:echo win_getid()

スプリットして開いたバッファを閉じます。

:quit

再度、画面スプリットして別バッファを開きます。
test_2.txtにしましょう。

:vs test_2.txt
:echo win_getid()

1002です。

さらに、画面スプリットしてみます。
test_3.txt

:vs test_3.txt
:echo win_getid()


一度割り振られたウィンドウIDは使われないみたいですね。

VimScript win_gotoid

ウィンドウIDが理解できてきたので、次はwin_gotoid使います。
ヘルプです。

win_gotoid({expr})                                      *win_gotoid()*
                Go to window with ID {expr}.  This may also change the current
                tabpage.
                Return 1 if successful, 0 if the window cannot be found.

成功すると1、失敗すると0が返るんですね。
使ってみます。

/tmp $ rm test*
/tmp $ for i in {1..5};do echo HELLO$i>test_$i.txt;done
/tmp $ vim test_*.txt
:echo win_getid()

win_gotoで今見えているウィンドウのID、1000を指定してみましょう。
戻り値を見るためにechoを使います。

:echo win_gotoid(1000)

1が表示されたので、成功ですね。

次は、存在しないウィンドウIDを指定してみます。1005を指定してみます。

:echo win_gotoid(1005)


0が表示されたので、失敗です。

win_gotoidを使ってタブを切り替えてみます。

/tmp $ rm test*
/tmp $ for i in {1..5};do echo HELLO$i>test_$i.txt;done
/tmp $ vim -p test_*.txt
:echo win_getid()

win_gotoidで他タブに切り替えることができるでしょうか。

:call win_gotoid(1001)

見事にタブが切り替わりました。
ウインドウIDを確認します。

:echo win_getid()

wincol カレントウィンドウのカーソルカラム位置

カレントウィンドウのカーソルのカラム位置を返してくれます。

引数を指定せずにVim起動。

/tmp $ vim

適当に文字列を作ります。

:0r!seq 10 | pr -t10

カーソルは、行頭にあります。wincolを実行すると何が返ってくるでしょうか。

:echo wincol()


1が返ってきました。

wを押して、カーソルを移動し、wincolを実行してみます。

:echo wincol()


8が返ってきました。

col カーソルカラム位置

適当に文字列を作ります。

:0r!seq 10 | pr -t10

そのままのカーソル位置で、col実行。

:echo col(".")


1ですね。

wを5回押して6の位置にカーソルを移動して、col実行。

:echo col(".")


カーソル位置33が返ってきました。

colの”.”以外を使った動きも確認していきます。

引数を指定せずにVim起動。

/tmp $ vim

適当に文字列を作ります。

:0r!seq 100 | pr -t10

“$”を使ってみます。

:echo col("$")

57と表示されました。

これって、改行も含めた位置でしょうか。
確認するため、 $キーで、カーソルを行末へ移動。

:echo col(".")


56が返りました。

winline カレントウィンドウのカーソル行位置

winline()

カーソル行を返す。

カーソルを3行めに移動します。

:echo winline()


3が返りました。

今フォーカスしているウィンドウのカーソル位置が返ります。

VimScript winheight

winheight使います。
ヘルプ

winheight({nr})                                         *winheight()*
                The result is a Number, which is the height of window {nr}.
                {nr} can be the window number or the |window-ID|.
                When {nr} is zero, the height of the current window is
                returned.  When window {nr} doesn't exist, -1 is returned.
                An existing window always has a height of zero or more.
                This excludes any window toolbar line.

まずは引数を指定せずに実行してみます。

/tmp $ vim
:echo winheight()


エラーになりました。

{nr}は省略できないんですね。0を指定すればcurrent windowになるようです。

:echo winheight(0)


23と返ってきましたが、合ってるでしょうか。
番号を表示してみます。

:0r!seq 100
:1

23行ですね。
次は、上下で画面スプリットしてみます。

:sp

それぞれwinheightを実行してみましょう。
上の画面から

:echo winheight(0)


11が返りました。
Ctrl-wwで下の画面にフォーカス。

:echo winheight(0)


10が返りました。

VimScript winwidth

今回はwinwidthです。

winwidth({nr})                                          *winwidth()*
                The result is a Number, which is the width of window {nr}.
                {nr} can be the window number or the |window-ID|.
                When {nr} is zero, the width of the current window is
                returned.  When window {nr} doesn't exist, -1 is returned.
                An existing window always has a width of zero or more.
/tmp $ vim
:echo winwidth(0)


80が返ってきましたが、タイトルバーにあるように80×24なので、widthは80文字ですね。

左右で画面スプリットします。

:vs

それぞれのwinwidthを確認。

:windo winwidth(win_getid())


それぞれのウィンドウ幅が返ってきました。

VimScript wincount

wordcount使います。

wordcount()                                             *wordcount()*
                The result is a dictionary of byte/chars/word statistics for
                the current buffer.  This is the same info as provided by
                |g_CTRL-G|
                The return value includes:
                        bytes           Number of bytes in the buffer
                        chars           Number of chars in the buffer
                        words           Number of words in the buffer
                        cursor_bytes    Number of bytes before cursor position
                                        (not in Visual mode)
                        cursor_chars    Number of chars before cursor position
                                        (not in Visual mode)
                        cursor_words    Number of words before cursor position
                                        (not in Visual mode)
                        visual_bytes    Number of bytes visually selected
                                        (only in Visual mode)
                        visual_chars    Number of chars visually selected
                                        (only in Visual mode)
                        visual_words    Number of words visually selected
                                        (only in Visual mode)

とりあえず無名バッファを起動して確認。

/tmp $ vim
:echo wordcount()

各項目全部0です。

HELLOと書いてみます。

:echo wordcount()

それらしい数値になりました。
数字ではどうでしょう。HELLOの代わりに12345に書き換えます。

:echo wordcount()


数字に変えても同じでした。

引数なしで起動。

/tmp $ vim

実験用テキストを作ります。

:0r!seq 100 | pr -t10

word数をカウントしてみます。
1から100までの数を並べただけなので、100になるはずです。

:echo wordcount().words

100と表示されました。

1個だけ消してみます。

:%s/45//

45を消してみました。

再度word数をカウントしてみます。

:echo wordcount().words

cursor_wordsです。

引数なしで起動して、実験用テキストを作ります。

/tmp $ vim
:0r!seq 100 | pr -t10

「:5」と入力して、5行目の行頭にカーソル移動後、cursor_wordsを表示してみます。

:echo wordcount().cursor_words


41と表示されました。

次にwキーを押して、カーソルを単語ひとつ右へ移動してみます。

再度cursor_wordsを表示してみます。

:echo wordcount().cursor_words


42になりました。

カーソル位置のwordを含んで、カーソル以前にどれだけwordがあるかカウントしてくれてるようです。

ggで、カーソルを、1行目の行頭に移動後、cursor_wordsを確認してみます。

:echo wordcount().cursor_words

1ですね。

次は、「:10」、$キーで、10行目の行末。

:echo wordcount().cursor_words

想定どおり100が返りました。

実験用テキストを作ります。

/tmp $ vim
:0r!seq 9

word数は、

:echo wordcount().words


当然9ですね。

byte数は、各行改行を入れて+1するので、9 x 2 = 18が返るはず。

:echo wordcount().bytes

あれ19になってます。

もしや、10行あるでしょうか。行番号表示してみます。

:set nu


10行目があります。

xxdを使って、メモリダンプを確認してみます。

:%!xxd


最後の改行を含んで、19byteのようです。

実験用テキストを作ります。

/tmp $ vim
:0r!seq 11 19

さらに10行目を削除

:10delete

byte数をカウントします。1行、改行を含めて3byteなので、3 x 9 = 27byteですね。

:echo wordcount().bytes

想定どおり27です。

空白はbyte数にカウントされるでしょうか。 数字の間に空白を入れてみます。

:%s/\v(.)/\1 /

見づらいのでハイライトはオフします。

:noh

では、byte数カウントします。

:echo wordcount().bytes


36と表示されました。空白もbyte数に含まれるってことですね。
ってことは、単にファイルサイズですね。

cursor_bytesを使います。
引数なしで起動して、実験用テキストを作ります。

/tmp $ vim
:0r!seq 11 19

さらに10行目を削除して、各行に空白を追加。

:10delete
:%s/\v(.)/\1 /

この状態でbytesを確認すると、

:echo wordcount().bytes


36ですね。

「gg」で先頭行の行頭にカーソルを移動します。そしてcursor_bytesを確認。

:echo wordcount().cursor_bytes


1ですね。

次に「3j」で4行目の行頭にカーソルを移動します。そしてcursor_bytesを確認。

:echo wordcount().cursor_bytes


13と表示されました。

1行が4byteで、カーソルの前に3行あるので、4 x 3 = 12Byte。
カーソルの下の文字を含めて+1。13Byteです。
「l」キーでカーソルを1文字右に移動して、再度cursor_bytesを確認。

:echo wordcount().cursor_bytes


14になりました。

arg関連

VimScript argc

argc関数使います。
ヘルプから。

argc([{winid}])
    The result is the number of files in the argument list.  See
    |arglist|.
    If {winid} is not supplied, the argument list of the current
    window is used.
    If {winid} is -1, the global argument list is used.
    Otherwise {winid} specifies the window of which the argument
    list is used: either the window number or the window ID.
    Returns -1 if the {winid} argument is invalid.

引数で指定したファイル数が分かるんですね。
早速ファイル名を指定して起動。

/tmp $ vim file1.txt


argcを実行してみます。

:echo argc()


引数で指定したファイルは1個だったので、1が返ってきました。
Vimを終了して、今度は複数ファイルで起動してみます。

/tmp $ touch test_{1..100}.txt
/tmp $ vim test_*.txt


Vimの最下段の通知を見ると、ファイルが1つしか開いてないように見えますが、
タイトルバーにファイルがたくさん並んでるので、ファイルを全部開いているのでしょう。
argcを実行する前に、lsを見てみます。

:ls


多すぎて表示し切れてないですね。
では、argc実行。

:echo argc

argcが起動した時の引数なら、bdeleteした後も、数は変わらないハズですが、確認してみましょう。

:echo argc()


1番のバッファをbdeleteします。

:bd 1


argcを確認。

:echo argc()


100のままですね。
次は、bwipeoutしてみます。

:bw 2

argcを確認。

:echo argc()


想定通り、100のままでした。
タイトルバーの表示も起動時から変わってないですね。

VimScript argidx

argidx関数を使ってみます。
ヘルプから。

argidx()        The result is the current index in the argument list.  0 is
                the first file.  argc() - 1 is the last one.  See |arglist|.

現在開いているファイルのインデックス番号ですね。
最初のファイルなら0。最後のファイルは、argc-1になるようです。 ファイルを100個開いて確認してみます。

/tmp $ touch test_{1..100}.txt
/tmp $ vim test_*.txt


起動直後なので、インデックス0のファイルを開いているハズです。

:echo argidx()


0ですね。
次のバッファへ切り替えます。

:next


バッファとファイルが紐ついているので、次のファイルに切り替わりました。
では、argidxを確認します。

:echo argidx()


インデックスは1が返ってきました。

argidxは起動時に指定したファイルのインデックスであることは分かりました。
やはりこれもバッファ削除するとどうなるか気になるので確認しておきます。

起動方法は同じ。

/tmp $ touch test_{1..100}.txt
/tmp $ vim test_*.txt


1から10のバッファをbdelete。

:bdelete 1 2 3 4 5 6 7 8 9 10


10バッファ削除したので、argidxを確認してみましょう。

:echo argidx()


え!?
0のままです。どういうことでしょう? lsを確認。

:ls


11番のバッファ(test_18.txt)がカレントバッファになってます。
想定ではargidxの結果は10になって欲しいところです。
バッファリストと引数リストは別物なんですね。
この状態で、次のバッファにしてみます。

:next


test_10.txtになったってことを、おそらくargidxは1が戻ります。

:echo argidx()


まあよくよく考えると、前々回に、バッファ削除後のargcを確認しましたが、引数の数は
変化しなかったので、当然の結果ですが、バッファ削除を使うのが怖くなりました。

VimScript :args

lsでバッファリストの確認ができますが、

:ls


バッファリストだけでなく、引数リストも確認してみましょう。
argsです。

:args


[]次のファイルが、カレントファイルということですね。
画面スプリットするとどうなるでしょうか。

:split
:next


このようにスプリットした状態で、argc確認。

:args

カレントバッファのみが[]が付くようです。

arglistを選択し直すには、args ファイルリスト を使います。
特定のファイルを指定してVimを起動してみます。

/tmp $ touch test_{1..5}.txt
/tmp $ vim test_2.txt


arglistを確認。

:args


当然一つだけですね。では、arglistを再度設定します。

:args test_*.txt


arglistを確認。

:args


argsを使いこなしてVim編集能力を向上したいです。

VimScript :argdelete

バッファ削除があるなら、引数削除もありますね。
argdeleteを使ってみます。
現在のargsの状態。

:args


ここからtest_1.txtを削除してみます。

:argdelete test_1.txt


argdeleteしても、画面ではtest_1.txtが、見えてますね。
argsを確認。

:args


引数リストからは削除されました。
バッファリストと引数リストがリンクしていないので、ものすごくややこしいです。
リンクさせない理由があるんでしょうけど。

/tmp $ touch test_{1..100}.txt
/tmp $ vim test_*.txt

最初にargs確認。

:args

全てのパターンを指定してargdeleteを実行します。

:argdelete *

特に何も変わってないように見えます。
argsを見てみます。

:args

全部消えてますね。

argcの数も0になっているでしょうか。

:echo argc()

パターンを指定して
バッチリ0になってます。少しずつですが、引数リストについて分かってきました。

VimScript arglist

bufferとargについてもう少し学習します。
引数のリスト、arglistを表示にするには、:argsを使います。
:argsで表示されるリストは、vim起動時の引数、つまり編集しているファイルのリストが表示されます。

/tmp $ touch test_{1..5}.txt
/tmp $ vim test_*.txt

bufferのリストは:lsで表示します。

:ls

argリストとbufferリスト同じ数ですね。

では、新規バッファを作成してみます。バッファの名前はtest_6.txtにします。

:new test_6.txt

スプリットされた上に配置されました。
bufferリストを確認してみます。

:ls

bufferリストは、5つから1つ増えて6つになってます。

次は、arglist。

:args

arglistの方は、5つのままです。

では、新規に追加したtest_6.txtを保存するとどうなるでしょう。

:write

保存後のarglistを確認。

:args


変化なしですね。

さきほど5つのファイルを引数に指定してVimを起動した後、新たにbufferを追加しました。

test_6.txtは、保存済みなので、このまま閉じます。
ZZ
画面が一つになったところで、現在のbufferを確認します。

:ls


%がtest_1.txtになってます。argsを見てみます。

:args


test_1.txtが括弧で括られているので、lsで選択されているbuffer名と同じですね。

:nextを実行してみます。

:next
:args


括弧が次のファイルに移動しました。
test_5.txtまで進めてみます。

:3next
:args


括弧がtest_5.txtに移動しました。
では現在のlsを確認。

:ls


%がtest_5.txtを指しているので、argsでの選択ファイルと一致しています。
では次。さらにnextするとどうなるか。

:next


エラーになりました。最後のファイルを超えて進めないようです。

arglistのヘルプを見てみます。

:h arglist

3. The argument list                            *argument-list* *arglist*

If you give more than one file name when starting Vim, this list is remembered
as the argument list.  You can jump to each file in this list.

Do not confuse this with the buffer list, which you can see with the
|:buffers| command.  The argument list was already present in Vi, the buffer
list is new in Vim.  Every file name in the argument list will also be present
in the buffer list (unless it was deleted with |:bdel| or |:bwipe|).  But it's
common that names in the buffer list are not in the argument list.

This subject is introduced in section |07.2| of the user manual.

There is one global argument list, which is used for all windows by default.
It is possible to create a new argument list local to a window, see
|:arglocal|.

You can use the argument list with the following commands, and with the
expression functions |argc()| and |argv()|.  These all work on the argument
list of the current window.

以前も使いましたが、arglistはargc()や、argv()といった関数が使えるんですね。

では適当にファイルを引数に指定してVim起動。

/tmp $ touch test_{1..5}.txt
/tmp $ vim test_*.txt

arglist確認。

:args


関数でarglistを取得してみます。

:echo argv()


この時のarglistの数は、

:echo argc()

VimScript :next

:nextコマンドのヘルプを見てみます。

:h next
:n[ext] [++opt] [+cmd] {arglist}                        *:next_f*
                        Same as |:args_f|.

:n[ext]! [++opt] [+cmd] {arglist}
                        Same as |:args_f!|.

arglistが対象であることがわかるので、バッファが存在していても、arglistに存在して
いないファイルへは移動できないようです。

では、argsになくて、lsに存在しているbufferへはどう移動すれば良いのでしょうか。

答えは簡単。bnextを使えば良いです。ただのnextだとarglist内の移動、bnextだとbufferlist内の移動ができます。

:bnext

bufferリストを確認。

:ls

では、この時のargsはどうなっているのか。

:args


なんと、test_5.txtが選択されたままです。ややこしいです。
argsとlsがずれたままVimを使うのは頭こんがらがります。

VimScript :argadd

arglistとbuffer-listが一致しないままVimを使用するのはまだ不安なので、両者を一致>させる方法を考えてみます。

今起動しているVimは、このように両リストは不一致しています。

:ls
:args

arglistに、test_6.txtが不足しているので、arglistにtest_6.txtを追加するだけですね
。 おそらくargをaddするコマンドがあると思います。ヘルプで見てみます。

:h argadd
:[count]arga[dd] {name} ..                      *:arga* *:argadd* *E479*
:[count]arga[dd]
                        Add the {name}s to the argument list.  When {name} is
                        omitted add the current buffer name to the argument
                        list.
                        If [count] is omitted, the {name}s are added just
                        after the current entry in the argument list.
                        Otherwise they are added after the [count]'th file.
                        If the argument list is "a b c", and "b" is the
                        current argument, then these commands result in:
                                command         new argument list
                                :argadd x       a b x c
                                :0argadd x      x a b c
                                :1argadd x      a x b c
                                :$argadd x      a b c x
                        And after the last one:
                                :+2argadd y     a b c x y
                        There is no check for duplicates, it is possible to
                        add a file to the argument list twice.
                        The currently edited file is not changed.
                        Note: you can also use this method:
                                :args ## x
                        This will add the "x" item and sort the new list.

使ってみます。

:argadd test_6.txt

順番は違いますが、リストの数は一致しました。

VimScript :first

arglistの先頭のファイルを選択するときに使います。
いつものように、適当に複数ファイルを起動。

/tmp $ touch test_{1..5}.txt
/tmp $ vim test_*.txt

arglist中の他のファイルを選択します。先頭以外ならどれでも良いです。

:3next

現在のarglistを確認。

:args

では:firstを実行してみます。

:first

先頭のファイルに切り替わったようです。arglistを確認してみます。

:args

:rewindってコマンドも:firstと同じです。
先頭以外のファイルに切り替えてから、

:2next
:args

:rewindを実行。

:rewind
:args


先頭ファイルになりました。

VimScript :last

複数ファイルを起動。

/tmp $ touch test_{1..5}.txt
/tmp $ vim test_*.txt

適当に最後以外のファイルに切り替えて、
(最初のファイルが選択されているのでいるので良いです)

:2next
:args

:last起動。

:last

arglistも最後のファイルが選択されています。

:args

VimScript :wnext

:nextコマンドで、arglistの次のファイルにバッファを切り替えすることができましたが、:wnextを使うと、保存してから切り替えることができます。

/tmp $ touch test_{1..5}.txt
/tmp $ vim test_*.txt

いつものように複数起動。

:args


現在のバッファには、最初のファイルが選択されています。
テキストに適当に書き込みます。
:wnextを実行。

:wnext


arglistを確認。

:args

保存してから、前のファイルに切り替えるには、:wNext(:wN)です。

:wNext

:wNextは、:wprevious(:wp)と同じです。Shift押さないで済むので、私は、こちらの方が
使いやすいです。

:last
:wprevious

VimScript :argdo

:bufdoを実行した直後に、:argdoを実行してみます。

前回:2,3bufo s/HELLO/HELL/ を実行したところからの続きです。

ではargdo実行。

:3,4argdo! s/LL/OO/

Enterを押してメッセージを消すと。

さらに:ball

:ball


bufdoで置換した後、バッファの保存は実行していませんが、argdoでバッファが初期状態に戻ることなく置換されました。

タブ関連

Vim起動時にタブ

vimの起動時オプション-pで、タブを開いた状態で起動できます。
-pに指定する数字は、最大タブ数です。

/tmp $ vim -p3 test_*.txt


タブが3つだけ表示されました。
このとき、:lsを見ればわかるように、タブ数とバッファ数は一致しません。

:ls

VimScript :tabnew

:tabnew使ってみます。

Vimを起動します。

/tmp $ vim

tabnew実行。

:tabnew

もう一度tabnew実行。

:tabnew

画面上部にNo Nameが3つ表示されて、一番右のNo Nameが黒背景に白文字になっていますが、これが現在選択中のタブ>を表しているようです。

次のタブへ切り替えるには、:tabn(ext)を使います。

:tabn

前のタブへ戻すには、:tabNです。

:tabN

/tmp $ for i in {1..5};do echo HELLO$i>test_$i.txt;done
/tmp $ vim test_1.txt

ファイルを指定してtabnewします。

:tabnew test_2.txt

さらにもう一回。

:tabnew test_3.txt

3つのファイルがそれぞれタブに割り当たりました。
buffer-listを確認してみます。

:ls


タブを追加したタイミングでバッファを追加されているようです。

/tmp $ for i in {1..5};do echo HELLO$i>test_$i.txt;done
/tmp $ vim test_*.txt

buffer-list確認。

:ls

全部バッファをタブで開きます。

:bufdo tabnew

Enterを押します。
いちばん最後に追加したNo Nameのタブを削除します。

:quit


出来上がり。

VimScript :tabmove

:tabmoveを使ってみます。
ヘルプを確認。

:h tabmove
:tabm[ove] [N]                                          *:tabm* *:tabmove*
:[N]tabm[ove]
                Move the current tab page to after tab page N.  Use zero to
                make the current tab page the first one.  N is counted before
                the move, thus if the second tab is the current one,
                `:tabmove 1` and `:tabmove 2`  have no effect.
                Without N the tab page is made the last one.
                    :.tabmove   " do nothing
                    :-tabmove   " move the tab page to the left
                    :+tabmove   " move the tab page to the right
                    :0tabmove   " move the tab page to the beginning of the tab
                                " list
                    :tabmove 0  " as above
                    :tabmove    " move the tab page to the last
                    :$tabmove   " as above
                    :tabmove $  " as above

移動したいタブ番号を指定すれば良いようです。
ではいつもの起動。

/tmp $ for i in {1..5};do echo HELLO$i>test_$i.txt;done
/tmp $ vim test_*.txt

そして、それぞれのバッファでタブを追加。

:bufdo tabnew


No Nameのタブを移動してみます。

:tabmove 1


2番目に移動しました。
ってことは、先頭に移動したい場合は、0を指定するんですね。

:tabmove 0


最後に移動するには、単にtabmoveだけですね。

:tabmove

VimScript :tabclose

タブを閉じる:tabcloseを使ってみます。
ヘルプを確認。

                                                        *:tabc* *:tabclose*
:tabc[lose][!]  Close current tab page.
                This command fails when:
                - There is only one tab page on the screen.             *E784*
                - When 'hidden' is not set, [!] is not used, a buffer has
                  changes, and there is no other window on this buffer.
                Changes to the buffer are not written and won't get lost, so
                this is a "safe" command.
                    :tabclose       " close the current tab page

カレントタブを消すだけですね。
ファイルを複数起動して:tabcloseを使ってみます。

/tmp $ for i in {1..5};do echo HELLO$i>test_$i.txt;done
/tmp $ vim test_*.txt

全バッファをタブで開きます。

:bufdo tabnew

カレントタブはNo Nameです。このタブで、tabclose使ってみます。

:tabclose

No Nameのタブは消えて、test_5.txtのタブが、カレントタブになりました。

:ls

次は、:tabcloseではなく、:quitで閉じてみます。

:quit

test_5.txtのタブは消えて、test_4.txtのタブが、カレントタブになりました。
buffer-listを確認。

:ls

:quitで同じことができるなら、:quitでいい気がします。

VimScript :tabdo

各タブでコマンド実行します。

:tabdo s/HELLO/HELL /

VimScript :tab

:tab help

VimScript :tabonly

:tabonly使ってみます。
ヘルプを確認。

                                                        *:tabo* *:tabonly*
:tabo[nly][!]   Close all other tab pages.
                When the 'hidden' option is set, all buffers in closed windows
                become hidden.
                When 'hidden' is not set, and the 'autowrite' option is set,
                modified buffers are written.  Otherwise, windows that have
                buffers that are modified are not removed, unless the [!] is
                given, then they become hidden.  But modified buffers are
                never abandoned, so changes cannot get lost.
                    :tabonly        " close all tab pages except the current
                                    " one

複数ファイルを開きます。

/tmp $ for i in {1..5};do echo HELLO$i>test_$i.txt;done
/tmp $ vim test_*.txt
:bufdo tabnew

カレントタブは、No Nameです。ここで:tabonlyを実行してみます。

:tabonly

タブが全て消えました。
buffer-listを確認。

:ls


カレントバッファは、No Nameのままですね。

VimScript :tabfind

:tabfind ファイル名

指定したファイルをタブで開きます。

/tmp $ for i in {1..5};do echo HELLO$i>test_$i.txt;done
/tmp $ vim

:tabfind test_3.txt

検索

:vimgrep (:vim) グローバル検索

:vimgrep /正規表現/ ファイル

:vimgrep /HELLO/ *.txt

最初に見つかった文字列が存在するファイルが開きます。

:copen (:cope) 結果一覧表示

検索結果一覧を表示します。

:copen

下の画面の検索結果からファイルを選択してEnterを押すと、上画面に選択したファイルが開きます。

:set hlsearch

検索文字列のハイライトON

:set nohlsearch

検索文字列のハイライトOFF

:nohでもOKです。

:find (:fin) ファイルを探して開く

複数ファイルを開いて:findを使ってみます。

/tmp $ for i in {1..5};do echo HELLO$i>test_$i.txt;done
/tmp $ vim test_*.txt

test_3.txtを探して開いてみます。

:find test_3.txt

test_3.txtが開いたようです。
buffer-listはどうなってるでしょう。

:ls

カレントバッファが、test_3.txtになってますね。
次は、バッファに存在しないファイルを指定するとどうなるか確認してみます。

/tmp $ vim


test_3.txtを探してみます。

:find test_3.txt


ファイルを見つけて開くことができました。

:lvimgrep (:lv) グローバル検索

vimgrepではquickfixリストを使いましたが、lvimgrepはロケーションリストを使います。
ロケーションリストはウィンドウ別に保持されます。

:vimgrepadd (:vimgrepa) 検索結果の追加

:vimgrepadd '[34]' test*


検索結果の一覧に追加されました。過去に行った同一検索の場合も一覧に追加されます。

その他の関数

type 型を返す

type(値)

戻り値は以下のとおり。

0:数値
1:文字列
2:Funcref
3:リスト
4:辞書
5:浮動小数点数
6:真偽値
7"特殊値
8"ジョブ
9:チャネル
10:Blob

数値

:let a=123
:echo type(a)

文字列

:let a="123"
:echo type(a)

リスト

:let a=[1,2,3]
:echo type(a)

辞書

:let a={1:'ONE', 2:'TWO', 3:'THREE'}
:echo type(a)

Blob

:let a=0z010203
:echo type(a)

sort ソート

sort(リスト)

:let a = [5,3,7,2,9,0]
:echo a

sort関数でソートしてみます。

:echo sort(a)

バッファ上のテキストの数列もソートしてみます。
いつものようにサンプル作成。

:0r!wsl seq 100 | wsl shuf | wsl pr -t5J

1から100までをランダムに並べてみました。
前回まで学習したmapとextendを組み合わせて、変数に取り込んだ後、sortします。

:let a = getline("w0","w$")
:call map(a,'split(v:val,"\t")')
:let b=[]
:call map(a,'extend(g:b,v:val)')
:echo b


変数に取り込めたので、sortしてみます。

:echo sort(b)

uniq 重複要素削除

uniq(リスト)

:let a = [5,3,7,2,9,0,3,5,2]
:echo sort(a)

2,3,5が重複していますね。uniq関数を実行してみます。

:echo uniq(sort(a))

Vimバッファ上のテキストでやってみます。
0~9のランダムの数値を100個ならべて、

:0r!wsl for i in `seq 100`;do expr $RANDOM \% 10;done | wsl pr -t5J


これをVimスクリプトで、sortしてuniqします。
まずは、取り込み。

:let a=getline("w0","w$")
:let out=[]
:call map(a,'extend(g:out,split(v:val,"\t"))')
:echo out

uniqは親切にも破壊的置換をしてくれるので、コピーしたリストの方をuniqします。

:let b = copy(out)
:call uniq(sort(b))
:echo b

VimScript filter

filterを使います。

前回は:lsの結果をmapを2回繰り返して、buffer-listのファイル名を取得しましたが、
もう少し簡単に抽出してみます。

/tmp $ touch test_{1..5}.txt
/tmp $ vim test_*.txt
:let arr=split(execute('ls'),'"')
:echo arr

あとは、配列の奇数番目を抽出すれば良いですね。

:let i=0
:while i < len(arr)
:  if i % 2 == 1
:    echo arr[i]
:    endif
:  let i=i+1
:  endwhile

これをfilterを使って、もっとシンプルにしてみます。

filterのヘルプを確認。

:h filter(
filter({expr1}, {expr2})                                *filter()*
                {expr1} must be a |List| or a |Dictionary|.
                For each item in {expr1} evaluate {expr2} and when the result
                is zero remove the item from the |List| or |Dictionary|.
                {expr2} must be a |string| or |Funcref|.

〜省略〜

                Example that keeps the odd items of a list:
                        func Odd(idx, val)
                          return a:idx % 2 == 1
                        endfunc
                        call filter(mylist, function('Odd'))
                It is shorter when using a |lambda|:
                        call filter(myList, {idx, val -> idx * val <= 42})
                If you do not use "val" you can leave it out:
                        call filter(myList, {idx -> idx % 2 == 1})

ちょうど奇数抽出のことがExampleで述べられています。
これを使ってみます。

:let a=filter(arr,{idx -> idx % 2 == 1})
:echo a

filter続きです。

テキストのフィルタにも使えますね。

/tmp $ vim
:0r!seq 20

:let arr=getline("w0","w$")
:echo arr

3つおきに抽出。

:let a=filter(arr,{idx -> idx % 3 == 0})
:echo a

テキストを置き換えます。

:call setline("w0w$",a)

下の方が削除されてません。
どうやら、全部削除してからsetlineする必要がありそうです。

バッファを全削除します。

:0,$d

再度setline。3行おきに抽出したテキストに置き換えます。

:call setline("w0",a)

VimScript glob

glob関数で、ワイルドカードが指定できます。

:echo glob("test_*.txt")

VimScript uniq sort


検索結果に対して、タブが多すぎですので、重複しているタブを削除するため、一旦、現在のタブを全て削除。

:tabnew
:tabonly

検索結果を今一度確認。

:echo a

別の空リストを用意します。

:let b=[]

ファイル名のみをbリストに登録。

:for bname in a
:  call add(b,bufname(bname.bufnr))
:  endfor
:echo b

次に、重複しているファイルを削除するため、sortしてuniqします。

:let c=uniq(sort(b))
:echo c

ファイル名一覧ができたので、各ファイルにタブを割り当ててみます。

:for bname in c
:  execute "tabnew" bname
:  endfor


だいぶスッキリしました。

VimScript getftype

getftype使います。

ヘルプ。

getftype({fname})                                       *getftype()*
                The result is a String, which is a description of the kind of
                file of the given file {fname}.
                If {fname} does not exist an empty string is returned.
                Here is a table over different kinds of files and their
                results:
                        Normal file             "file"
                        Directory               "dir"
                        Symbolic link           "link"
                        Block device            "bdev"
                        Character device        "cdev"
                        Socket                  "socket"
                        FIFO                    "fifo"
                        All other               "other"
                Example:
                        getftype("/home")
                Note that a type such as "link" will only be returned on
                systems that support it.  On some systems only "dir" and
                "file" are returned.  On MS-Windows a symbolic link to a
                directory returns "dir" instead of "link".

ファイルの種類が返るだけですね。

使ってみます。

/tmp $ touch test1.txt
/tmp $ vim -c 'echo getftype("test1.txt")'


fileと表示されました。

ディレクトリも確認してみます。

:echo getftype("/tmp")

linkと表示されています。ディレクトリではないんでしょうか。

/tmp $ ls -l /tmp
lrwxr-xr-x@ 1 root  admin  11 10 13 12:48 /tmp -> private/tmp
/tmp $


リンクのようです。
では、新規にディレクトリを作成して確認してみます。

/tmp $ mkdir test1
/tmp $ vim -c 'echo getftype("test1")'


dirと返ってきました。

VimScript getfsize

getfsize使います。

いつものヘルプ。

getfsize({fname})                                       *getfsize()*
                The result is a Number, which is the size in bytes of the
                given file {fname}.
                If {fname} is a directory, 0 is returned.
                If the file {fname} can't be found, -1 is returned.
                If the size of {fname} is too big to fit in a Number then -2
                is returned.

ふむふむ。ファイルサイズですね。

使ってみます。

/tmp $ echo -ne "12345" > test1.txt
/tmp $ wc -c test1.txt
       5 test1.txt
/tmp $

5が表示されました。

以前使ったwordcountではどうでしょう。

/tmp $ vim -c 'echo wordcount().bytes' test1.txt

6が返りました。勝手に改行が入るようです。
xxdで確認してみます。

:%!xxd


改行0aが入ってますね。
vimを終了して、odコマンドで確認してみると、

/tmp $ od -tx1 test1.txt


5バイトです。

VimScript getfperm

getfperm使います。

getfperm({fname})                                       *getfperm()*
                The result is a String, which is the read, write, and execute
                permissions of the given file {fname}.
                If {fname} does not exist or its directory cannot be read, an
                empty string is returned.
                The result is of the form "rwxrwxrwx", where each group of
                "rwx" flags represent, in turn, the permissions of the owner
                of the file, the group the file belongs to, and other users.
                If a user does not have a given permission the flag for this
                is replaced with the string "-".  Examples:
                        :echo getfperm("/etc/passwd")
                        :echo getfperm(expand("~/.vimrc"))
                This will hopefully (from a security point of view) display
                the string "rw-r--r--" or even "rw-------".

                For setting permissions use |setfperm()|.

/tmp $ ls -l test1.txt
-rw-r--r--  1 takk            wheel  5 10 19 20:23 test1.txt
/tmp $


一致しますね。

/tmp $ chmod 444 test1.txt
/tmp $ ls -l test1.txt
-r--r--r--  1 takk            wheel  5 10 19 20:23 test1.txt
/tmp $

リードオンリーに変更しました。

一致しました。
戻しておきます。

/tmp $ chmod 644 test1.txt
/tmp $ ls -l test1.txt
-rw-r--r--  1 takk            wheel  5 10 19 20:23 test1.txt
/tmp $

っていうか戻せるんですね。

VimScript empty

empty使います。
ヘルプの説明を読みます。

empty({expr})                                           *empty()*
                Return the Number 1 if {expr} is empty, zero otherwise.
                - A |List| or |Dictionary| is empty when it does not have any
                  items.
                - A |String| is empty when its length is zero.
                - A |Number| and |Float| are empty when their value is zero.
                - |v:false|, |v:none| and |v:null| are empty, |v:true| is not.
                - A |Job| is empty when it failed to start.
                - A |Channel| is empty when it is closed.
                - A |Blob| is empty when its length is zero.

                For a long |List| this is much faster than comparing the
                length with zero.

String、則ち文字列から確認します。
vimを起動して、

/tmp $ vim
:echo empty("HELLO")

0が返りました。
1が真なので、HELLOは空ではないから合ってますね。
空を指定してみます。

:echo empty("")

1になりました。空ってことですね。

変数を使って確認します。

:let a="HELLO"
:echo empty(a)


0が返りました。

空。

:let a=""
:echo empty(a)


1が返りました。

数字をemptyの引数に指定した時の結果を確認します。

:let a=100
:echo empty(a)

0なので空ではない意味です。

数字を0にすると、

:let a=0
:echo empty(a)


1が返りました。空と判定されたってことですね。

次は小数点付き。

:let a=100.5
:echo empty(a)

0なので空ではないですね。

次は1より小さい数。

:let a=0.5
:echo empty(a)


0ですね。空と判定されてません。

数字の0のみがemptyってことですね。

listのempty判定です。
起動から。

/tmp $ vim -c 'let a=[1,2,3]'
:echo a

emptyの結果は、

:echo empty(a)

リストに要素が詰まっているので、emptyの結果は、False(0)ですね。
次は、リストを空にしてみます。

:let a=[]
:echo a

emptyの結果は、

:echo empty(a)


True(1)になりました。空ですね。

Dictionaryです。

Vimを起動します。

/tmp $ vim -c "let a={1:'one',2:'two'}"

起動後、Dictionaryを確認。

:echo a.1

emptyを使います。

:echo empty(a)


0なので、空ではないです。

次は、Dictionaryを空にしてみます。

:let a={}
:echo a.1


エラーも出ているし、空ですね。

emptyで確認します。

:echo empty(a)

VimScript filereadable

ファイルが読めるかチェック。

実験してみます。
まずファイルを作ります。

/tmp $ echo HELLO > file1.txt
/tmp $ ls -l file1.txt
-rw-r--r--  1 takk            wheel  6 11 15 02:01 file1.txt
/tmp $
/tmp $ vim -c 'echo filereadable("file1.txt")'

左下に1が表示されました。1はTRUEなので、ファイルが読み込み可能ってことです。

存在しないファイルを指定してみます。

:echo filereadable("aaa.txt")

0が表示されました。
以前使ったgetfpermと似てますね。

VimScript filewritable

ファイルが書き込めるかのチェック。

/tmp $ ls -l file1.txt
-rw-r--r--  1 takk            wheel  6 11 15 02:01 file1.txt
/tmp $
/tmp $ vim -c 'echo filewritable("file1.txt")'

1が表示されたのでTRUEですね。書き込み可能です。

次はもちろん。存在しないファイル。新規のファイルを作成することができるのかチェックなら、1が返るハズ。

:echo filewritable("aaa.txt")

0ですね。ファイル作成が可能かではなく、あくまでも存在するファイルが書き込み可能かどうかのチェックのようで>す。

次は存在している書き込み禁止のファイルを指定してみます。

/tmp $ ls -l /bin/ls
-rwxr-xr-x  1 root  wheel  51888 10 24 10:34 /bin/ls
/tmp $

これにします。

:echo filewritable("/bin/ls")


0が表示されました。

コメント

タイトルとURLをコピーしました