概要
Goでは簡単にコマンドラインツールが作れますが、人によって引数やオプションといったインタフェースがバラバラになりがちです。
POSIX Utility Syntax Guidelinesというガイドラインがあるので、これに則るとUnixライクな統一されたインタフェースのCLIツールになります。
環境
- golang/go v1.15.1
インタフェースのイメージ
例えば文字列の入ったファイルがあり
$ cat sample.txt hogefuga
入力文字列をすべて大文字にするコマンドcapitalize
を作る場合、以下の3つのインタフェースをサポートするイメージです。
$ cat sample.txt | capitalize HOGEFUGA $ cat sample.txt | capitalize - HOGEFUGA $ capitalize sample.txt HOGEFUGA
具体的なコード
では具体的にどういった処理をするかをコードで説明します。
var filename string if args := flag.Args(); len(args) > 0 { filename = args[0] } var r io.Reader switch filename { case "": if terminal.IsTerminal(int(syscall.Stdin)) { return nil, errors.New("usage: capitalize path") } r = os.Stdin case "-": r = os.Stdin default: f, err := os.Open(filename) if err != nil { return nil, err } defer f.Close() r = f } b, err := ioutil.ReadAll(r) if err != nil { return nil, err } return b, nil
ロジックとポイント
ロジックは以下です。
flag.Args()
でコマンドライン引数をチェックする- 空の場合、
terminal.IsTerminal()
で入力漏れかパイプで渡しているかをチェック- パイプの場合は標準入力(
os.Stdin
)を使う - 入力漏れの場合はエラー扱い
- パイプの場合は標準入力(
-
の場合、ガイドラインに則って標準入力(ただし標準出力として扱うケースもあります)- コマンドライン引数が存在する場合はそれを使う
ポイントとしては以下です。
terminal.IsTerminal()
でパイプかターミナル入力かをチェックterminal.IsTerminal()
の引数はWindowsでエラーにならないよう、intでキャスト
コード全体
コード全体は以下のリポジトリにあります。
標準入力に対応してると何が嬉しい?
標準入力や標準出力に対応させているとどんなメリットがあるでしょうか。
Unix系コマンドでパイプを使って色んな組み合わせができる
Unix系コマンドは非常に洗練されたツールばかりです。
わざわざプログラムを書かずともUnix系コマンドを利用するだけで要件を満たせることも多々あります。
そんなUnix系コマンドと連携できるのは大きなメリットです。
$ cat sample.txt aaa bbb ccc ddd eee $ cat sample.txt | ./capitalize | grep -e "CC" CCC
Macだとpbcopy/pbpasteと連携できる
標準出力対応していればpbcopy
を使ってクリップボードに保存できます。
$ echo "abcde" | ./capitalize | pbcopy
逆にpbpaste
を使えばクリップボードにあるデータを標準入力として使えるので、わざわざファイルとして用意せずともそのままコマンドに流せます。
$ pbpaste | ./capitalize ABCDE
まとめ
CLIツールのインタフェースデザインの1つとして、標準入力とファイル読み込みに対応したケースを紹介しました。