다양한 cli가 존재하고 요즘은 Sub command 형태로 cli를 확장하는 것이 대세인 듯하다. Devocean에도 ‘Cobra를 이용한 CLI 유틸리티 만들기**’(**https://devocean.sk.com/search/techBoardDetail.do?ID=163440)라는 주제로 개요에대해 다루고 있다. 실제 cli를 구현하다보면 맞닥드리게되는 다양한 옵션에 대한 소개가 부족한 듯하여 정리해 보려고 한다.
본 페이지에서는 중심이되는 cobra의 command 객체에 대해 다뤄본다.
기본적으로 ‘cobra init’ 명령을 사용하여 기본 cobra기반 프로젝트를 생성한다. 이때 ‘-l’ 옵션으로 라이센스관련 정보를 ‘-a’ 옵션으로 저자 관련 정보를 지정하여 시작할 수 있다. 명령이 수행되면 $GOPATH/src 아래에 생성된 자원들을 확인할 수 있다.
$ cobra init tks -a "SK Telecom" -l MIT
Your Cobra application is ready at
/home/hereisgopath/src/tks.
Give it a try by going there and running `go run main.go`.
Add commands to it by running `cobra add [cmdname]`.
$ tree $GOPATH/src/tks
/home/hereisgopath/src/tks
├── cmd
│ └── root.go
├── LICENSE
└── main.go
1 directory, 3 files
앞에서 만들어진 프로젝트로 이동하여 ‘cobra add’ 명령을 사용하면 sub command를 위한 구조와 기본 내용이 포함된 코드들이 반영된다.
$ cd $GOPATH/src/tks
$ cobra add test
구체적으로 디렉토리 ‘cmd’에 지정한 명령어 이름으로 파일이 생성되고 (위 예제에서는 cmd/test.go) 기본 코드를 포함하고 있다.
앞에서 init시 만들어진 cmd/root.go 파일을 살펴보면 다음과 같다. (수많은 주석과 함께 만들어지지만 편의를 위해 주석부분은 삭제했음)
package cmd
import (
"fmt"
"os"
homedir "github.com/mitchellh/go-homedir"
"github.com/spf13/cobra"
"github.com/spf13/viper"
)
var cfgFile string
var RootCmd = &cobra.Command{
Use: "tks",
Short: "A brief description of your application",
Long: `A ..... application.`,
}
func Execute() {
if err := RootCmd.Execute(); err != nil {
fmt.Println(err)
os.Exit(1)
}
}
func init() {
cobra.OnInitialize(initConfig)
RootCmd.PersistentFlags().StringVar(&cfgFile, "config", "", "config file (default is $HOME/.tks.yaml)")
RootCmd.Flags().BoolP("toggle", "t", false, "Help message for toggle")
}
func initConfig() {
if cfgFile != "" {
// Use config file from the flag.
viper.SetConfigFile(cfgFile)
} else {
// Find home directory.
home, err := homedir.Dir()
if err != nil {
fmt.Println(err)
os.Exit(1)
}
viper.AddConfigPath(home)
viper.SetConfigName(".tks")
}
viper.AutomaticEnv() // read in environment variables that match
// If a config file is found, read it in.
if err := viper.ReadInConfig(); err == nil {
fmt.Println("Using config file:", viper.ConfigFileUsed())
}
}
각 부분은 다음과 같은 의미를 갖는다.