年中アイス

いろいろつらつら

Goでファイルの特定位置から読む

bashで組んでたログの検知スクリプトを、メンテナンス性と拡張性考えてGoで書き直すことにしました。 ファイルを定期的にtailして一定行数読み込んで処理する方式を取っていましたが、最後に読んだとこから後にしたいと調べていたら、Mackerelが似たようなことをやっていたので参考にしました。

go-check-plugins/check-log.go at master · mackerelio/go-check-plugins · GitHub

File.Seek()を使って、ファイルの特定バイトから読むようにできます。 前回読み込んだバイト数を記録しておけば、次はその後から追加されたデータのみを読むことができます。

whence*1が、ぱっと見何かわからなかったのですが、この値によってoffsetの使われ方が変わります。

whence 意味
0 ファイルの先頭からのoffset(先頭からスキップするバイト数)
1 今のSeek位置からのoffset(前回Seekした位置からスキップするバイト数)
2 ファイルの末尾からのoffset(末尾から読む。この場合は負数にしないと読めません)

サンプル

Readだけ動作試してみました。

一部抜粋

data

File seek in Go.

コード

fp, err := os.Open("./data")
if err != nil {
    panic(err)
}
defer fp.Close()

fp.Seek(5, 0)
b, err := ioutil.ReadAll(fp)
if err != nil {
    panic(err)
}
fmt.Printf("offset 5, whence 0: %s\n", string(b))

出力

offset 5, whence 0: seek in Go.

whence=2の時にoffsetをマイナスにしてなくて、読めないなーと勘違いしてました。今回試したのはReadですがWriteも同様に位置指定して書き込めるようです。その際は、os.Open()はReadOnlyなので、os.OpenFile(), os.Create()あたりで書き込める状態で作る必要があります。

*1:whenceあんまり聞かない単語でGoogleに投げると「そこから」と訳されました。どこから?の区分値と捉えると納得