[Go] flag패키지를 사용해서 커맨드라인 툴 만들어보기

flag? os? 커맨드라인 툴에서 인자 받기.

Command line 툴로 인자를 받는 두가지 방법이 있다.

  1. os 패키지의 사용
  2. flag 패키지의 사용

os 패키지의 사용

os 패키지는 굉장히 많은 함수들을 가지고 있지만 여기서 살펴볼 것은 Variables에 있는 Args string 슬라이스이다. 패키지 문서에 나와있는 설명처럼 Args 는 프로그램의 이름을 포함한 모든 command-line 인자들을 가지고 있다.

자, 이제 아래와 같은 코드를 작성해서
go build -o args
args 라는 이름으로 빌드한 후,
./args test argument 1
로 실행해보자.

args := os.Args
fmt.Println(args)

Result
[./args test argument 1]

와 같은 결과를 보여준다. 여기서 알 수 있는 것은 첫번째 인자는 언제나 프로그램의 이름이라는 것이다.

arg1 := args[0]   // 첫번째 인자는 프로그램의 이름을 반환한다.
fmt.Println(arg1)

Result
./args

이제 나머지 인자들을 구해보자. 나머지 인자들은 첫번째 인자인 프로그램의 이름을 제외함으로써 얻을 수 있다.

restArgs := args[1:]
fmt.Println(restArgs)

Result
[test argument 1]

또한 이 인자들을 for loop을 사용해서 개별적으로 얻어내보자.

for idx, arg := range restArgs {
	fmt.Printf("%d번째 인자: %s\n", idx, arg)
}

Result
0번째 인자: test
1번째 인자: argument
2번째 인자: 1

이렇게 os 패키지를 사용해서 인자를 얻을 수 있다.

전체 코드는 이렇다.

package main

import (
	"fmt"
	"os"
)

func main() {
	args := os.Args
	fmt.Println(args)
	
	arg1 := args[0]   // 첫번째 인자는 프로그램의 이름을 반환한다.
	fmt.Println(arg1)

	restArgs := args[1:]
	fmt.Println(restArgs)

	for idx, arg := range restArgs {
		fmt.Printf("%d번째 인자: %s\n", idx, arg)
	}
}

이제 바로 flag 패키지를 사용해서 인자를 얻는 방법을 알아보자.


flag 패키지의 사용

flag 패키지는 command-line flag, 즉, 인자들을 파싱할 수 있게 도와주는 패키지다. 일단 flag 패키지에 있는 func Int64 함수를 살펴보자. 이 함수는 int64 포인터를 반환하는 함수다.

maxValue := flag.Int64("max", 10, "Defines maximum value")

flag.Int64 함수는 특정 이름과 기본값, 그리고 사용법을 파라미터로서 요구한다.
반환값으로는 flag값에 저장되어 있는 int64 변수의 주소값(포인터)을 반환한다.

flag 패키지에 있는 다른 함수로 flag.Int, flag.Float64, flag.String, flag.UInt64 등 수많은 함수들이 있으니 위의 링크를 타고 들어가서 살펴보기 바란다.

이 함수들은 또한 OOOVar 라는 이름의 (OOO에는 Int, String, Bool, UInt64등의 자료형 이름이 들어가는 규칙이… 있다?) 함수들이 있는데 이 함수들을 사용할 경우에는 반드시 해당 변수를 flag 함수를 사용하기 전에 정의해두어야 한다. 이 함수들은 반환값이 없고 정의된 변수의 주소값(포인터)를 제공해야 한다. 이렇게 제공된 포인터를 타고 파싱된 flag 값을 변수에 할당하게 되는 것이다.

var minValue int64
flag.Int64Var(&minValue, "min", 0, "Defines minimum value")

또한 Go 라이브러리는 custom flag 타입을 지원한다. Custom 타입은 반드시 flag 패키지의 Value 인터페이스 를 구현해야 한다.

type StringArray []string

func (arr *StringArray) String() string {
	return fmt.Sprintf("%v", *arr)
}

func (arr *StringArray) Set(s string) error {
	*arr = strings.Split(s, ",")
	return nil
}

이렇게 인터페이스를 구현하면 Var 함수 를 통해 flag 값을 custom flag 타입의 변수에 할당할 수 있다.

var arrStrs StringArray
flag.Var(&arrStrs, "array", "An inputted array that can be iterated")

자, 이제 정말 중요한 작업이 하나 남았다. flag.Parse() 함수를 호출해야만 flag 패키지가 정말로 제 할일을 하게 될것이다.

flag.Parse()

이 함수는 os.Args[1:] 를 통해 command-line 인자들을 파싱해 온다. 이 함수는 특히나 중요한데 이 함수는 반드시 모든 flag들이 정의된 후에, 그리고 flag들이 프로그램에 의해 사용되기 전에 호출해야 한다. 그렇지 않으면 모든 변수들은 비어있는 상태로 남아버린다.

이제 전체 코드를 빌드해서 실행해보자.
go build -o testFlag
로 빌드해서
./flagArgs -max=20 -min=15 -array=Hello,World!
로 실행을 하면,

Result
[Hello World!]
[Hello World!]
[Hello World!]
[Hello World!]
[Hello World!]

와 같은 결과가 나타난다!

아래는 전체 코드다.

package main

import (
	"flag"
	"fmt"
	"strings"
)

type StringArray []string

func (arr *StringArray) String() string {
	return fmt.Sprintf("%v", *arr)
}

func (arr *StringArray) Set(s string) error {
	*arr = strings.Split(s, ",")
	return nil
}

func main() {
	maxValue := flag.Int64("max", 10, "Defines maximum value")

	var minValue int64
	flag.Int64Var(&minValue, "min", 0, "Defines minimum value")

	var arrStrs StringArray
	flag.Var(&arrStrs, "array", "An inputted array that can be iterated")

	flag.Parse()

	for minValue < *maxValue {
		fmt.Printf("%v\n", arrStrs)
		minValue++
	}
}

이렇게 flag나 os 패키지를 이용하면 command-line 툴에서 사용할 수 있는 setting 등 여러가지 옵션을 가지고 프로그램을 유저의 입맛에 맞게 실행시킬 수 있는 방법이 생긴다. 이 밖에도 굉장히 많은 함수들이 두 패키지 안에 있으니 공식 홈페이지에 가서 살펴보길 바란다.

comments powered by Disqus