タイトルをうまい感じにまとめられなくてそのままなタイトルになってしまったw
最近 Go のスキルをあげるのとちょっとほしい CLI があって作ってます。
その中でユーザーからの入力を echo --message hogehoge
のように実行時に引数として文字列を受け取るコマンドを作ってました。
ただこれはある程度の文字数と複数行が想定される引数です。
さすがにコマンドラインの引数として複数行わたしていくのは面倒そうだなーと思い良い案がないか考えてました。
自分が使っている身近なツールとかで解決していないかなと探した時にそういえば tig
がやりたいことに近いことをしていたのでソースを見てみました。
tig は Text-mode interface for git
と公式には書いてあってターミナルで良い感じに git を操作できるツールで、 commit する時に Editor が開くのですが、これがやりたいことに近かったです。
サーと読んだ感じと挙動をみた感じ EDITOR
とか VISUAL
のような環境変数からどのeditor で起動するかを判定してこれを使って .git/COMMIT_EDITMSG
開いている。
エディタで保存し終わると再度このファイルを読み込んで後続の処理をしている感じだった。
https://github.com/jonas/tig/blob/89667b4afb17b0535c64aab86fa0770451b076fe/src/display.c#L125
まぁーなるほどねーという感じ。
とりあえず Go でサンプルなコードを書いた。
package main import ( "fmt" "io/ioutil" "log" "os" "os/exec" ) const TEMPLATE = `## Title1 ## Title2 ` func main() { tmpfile, err := ioutil.TempFile("", "hogehoge") if err != nil { log.Fatal(err) } defer os.Remove(tmpfile.Name()) defer tmpfile.Close() if _, err := tmpfile.Write([]byte(TEMPLATE)); err != nil { log.Fatal(err) } if err := openEditor(tmpfile.Name()); err != nil { log.Fatal(err) } content, err := ioutil.ReadFile(tmpfile.Name()) if err != nil { log.Fatal(err) } fmt.Printf("%s", content) } func openEditor(filepath string) error { cmdName := "vi" if e := os.Getenv("EDITOR"); e != "" { cmdName = e } c := exec.Command(cmdName, filepath) c.Stdin = os.Stdin c.Stdout = os.Stdout c.Stderr = os.Stderr return c.Run() }
処理として tmp file を作成してそれにあらかじめ決めておいたテンプレートを流し込む。
その後に指定された editor で開いて書き込んだ後に保存したりして editor のプロセスを終了するとその後の処理で再度ファイルを読み込んで中身を出力する。
実際に実行すると↓のようにテンプレートを流した状態のファイルを編集するためのエディタが立ち上がる。
編集して保存すれば内容が出力される。