「任意の文字列や行をハイライトするツールをGo言語で作成してみた」でgrepコマンドの-Bオプションと似た動作をするプログラムが必要となったのでその作り方のメモを残しておく。
何が必要になったか
以下のようなfile.txtが存在した場合に
file.txt
message1
Hello World.
message2
hello world.
message3
HELLO WORLD.
一致した行とその前の2行も出力するgrepコマンドを実行した場合、以下の結果が得られる
$ grep -B 2 hello file.txt
Hello World.
message2
hello world.
これと似た動作をするプログラムが必要となった。
作成した関数
上記の動作と似た動作をする関数は以下の通り。
// pattern 検索対象文字列
// lines   ファイルを一行ずつ読み込んだ結果が流れてくるチャンネル
func grepBefore(pattern, lines chan string){
    n := 2 // 保持しておく行数
    buf := []string{} // 保持用のスライス
    for line := range lines {
        // 指定保持数を超えたら一番前の行を保持用スライスから削除
        if len(buf) > n {
            buf = buf[1:]
        }
        buf = append(buf, line)
        // 現在の行にpatternが含まれているならば、出力処理を行う
        if strings.Contains(line, pattern) {
            for _, l := range buf {
                fmt.Println(l)
            }
            // bufを空にする
            buf = buf[:0]
        }
    }
}
動作順序
- チャンネル(lines)からデータ(line)を読み取る。クローズされた場合は終了
 - バッファ(buf)にデータを格納
 - データに検索対象文字列(pattern)が含まれない場合は、1へ戻る
 - バッファに格納されている全ての行を出力する
 - バッファを空にする
 - 1へ戻る
 
おわりに
実際に必要となったのは、検索対象文字列の前後の行に色を付けて出力する処理だったので、もう少し複雑になりました。
そのコードは https://github.com/x-color/hlt/blob/master/cmd/hlt/highlight.go#L135 にあるので見てみたい方がいたらどうぞ。