LoginSignup
8
8

More than 5 years have passed since last update.

Golangの標準パッケージで簡単なCLIツールを作ってみる

Posted at

やること

Golangの標準パッケージであるflagを使って簡単なコマンドラインツールを作ってみます。

flagパッケージ

flagはオプションの解析やサブコマンド、ヘルプメッセージなど
コマンドラインツールの処理を簡単に実装できるような機能が提供されています。

まずは基本的な使い方から

package main

import (
  "fmt"
  "flag"
)

func main() {
  // 左から順にオプション名、デフォルトの値、helpテキストが引数に入る
  s := flag.String("s", "Hello, world!", "String help message")
  flag.Parse()
  fmt.Println(*s)
}

実行結果

$ go run example.go
Hello, world!

# -sオプションを付与して実行してみる
$ go run example.go -s foobar
foobar

# -hでヘルプメッセージが表示される
$ go run example.go -h
Usage of example:
  -s string
        String help message (default "Hello, world!")
exit status 2

型へのマッピング

flagパケージにはコマンドラインから入力された値をGoの型へマッピングする機能があります。

func main() {
  strCmd := flag.String("s", "Hello, world!", "String help message")
  intCmd := flag.Int("i", 1, "Int help message")
  boolCmd := flag.Bool("b", false, "Bool help message")

  flag.Parse()

  fmt.Println("str: ", *s)
  fmt.Println("int: ", *i)
  fmt.Println("bool: ", *b)
}

実行結果

$ go run example.go
str: Hello, world!
int: 1
bool: false

# それぞれのオプションを付与して実行する
$ go run example.go -s foobar -i 26 -b
str: foobar
int: 26
bool: true

# -iはint型を受け取るので整数以外を渡すとエラーになります
$ go run example.go -i foo
invalid value "foo" for flag -i: strconv.ParseInt: parsing "foo": invalid syntax
Usage of example:
  -b    Bool help message
  -i int
        Int help message (default 1)
  -s string
        String help message (default "Hello, world!")
exit status 2

ヘルプメッセージのカスタマイズ

ヘルプメッセージをより細かく書くこともできます。

import (
  "fmt"
  "os"
  "flag"
)

func main() {
  flag.Usage = func() {
    usageTxt := `Usage example [option]
An example of customizing usage output

    -s, --s  STRING argument, default: String help message
    -i, --i  INTEGER argument, default: Int help message
    -b, --b  BOOLEAN argument, default: Bool help message`

    fmt.Fprintf(os.Stderr, "%s\n", usageTxt)
  }
  // 以下略
}

実行結果

$ go run example.go -h
Usage example [option]
An example of customizing usage output

    -s, --s  STRING argument, default: String help message
    -i, --i  INTEGER argument, default: Int help message
    -b, --b  BOOLEAN argument, default: Bool help message
exit status 2

サブコマンドを作成する

git cloneのようなサブコマンドを簡単に作れる機能も提供されています。

// 最初の引数にサブコマンドの名前を、2番目にエラーハンドリングを指定する
subCmd := flag.NewFlagSet("subcmd", flag.ExitOnError)

// エラーハンドラの種類
// flag.ContinueOnError => エラーが発生しても処理を続ける
// flag.ExitOnError     => エラーが発生したらステータスコード2として終了する
// flag.PanicOnError    => ランタイムパニックを発生させる

なんか作ってみる

上記の機能を利用して作成したものが以下になります。
機能的には10進数を2進数か16進数へ変換するだけです。

package main

import (
  "fmt"
  "flag"
  "os"
  "strconv"
)

func flagUsage() {
  usageText := `example is an example cli tool.

Usage:
example command [arguments]
The commands are:
convhex    convert Number to Hex
convbinary convert Number to binary
Use "Example [command] --help" for more infomation about a command`

  fmt.Fprintf(os.Stderr, "%s\n\n", usageText)
}

func main() {
  flag.Usage = flagUsage
  convHexCmd := flag.NewFlagSet("convhex", flag.ExitOnError)
  convBinaryCmd := flag.NewFlagSet("convbinary", flag.ExitOnError)

  if len(os.Args) == 1 {
    flag.Usage()
    return
  }

  switch os.Args[1] {
  case "convhex":
      i := convHexCmd.Int64("i", 0, "Convert number to hex")
      convHexCmd.Parse(os.Args[2:])
      fmt.Println(strconv.FormatInt(*i, 16))
    case "convbinary":
      i := convBinaryCmd.Uint64("i", 0, "convert number to binary")
      convBinaryCmd.Parse(os.Args[2:])
      fmt.Println(strconv.FormatUint(*i, 2))
    default:
      flag.Usage()
  }
}

実行結果

$ go run example.go convhex -i 11111
2b67
$ go run example.go convbinary -i 151
10010111

$ go run example.go --help
example is an example cli tool.

Usage:
example command [arguments]
The commands are:
convhex    convert Number to Hex
convbinary convert Number to binary
Use "Example [command] --help" for more infomation about a command
8
8
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
8
8