• 흐림동두천 4.5℃
  • 구름많음강릉 8.6℃
  • 흐림서울 5.4℃
  • 흐림대전 5.4℃
  • 흐림대구 8.0℃
  • 흐림울산 8.6℃
  • 흐림광주 6.6℃
  • 흐림부산 9.8℃
  • 흐림고창 4.0℃
  • 흐림제주 11.0℃
  • 구름많음강화 3.1℃
  • 흐림보은 3.1℃
  • 흐림금산 4.4℃
  • 흐림강진군 6.9℃
  • 흐림경주시 6.1℃
  • 흐림거제 10.5℃
기상청 제공

programming

쉘스크립트로 인수 넘기기


출처 : http://nacarat.tistory.com


bash 쉘스크립트로 어떤 옵션 즉 스크립트의 동작을 변경하기 위한 플래그를 사용할 수 있도록 하고 싶다고 하자. 이러한 경우 ${#} 을 사용해 주어진 인수의 갯수를 얻거나 ${1:0:1} 을 사용해 최초 인수의 최초 문자가 「-」인지를 판정 하는 방법으로 직접 자신이 넘길수도 있지만, 자신이 넘기는 경우에는 주어진 옵션이 무엇인지, 인수를 요구하고 있는지 어떤지를 확인하기 위해 if/then 또는 case 등을 사용한 코드를 더할 필요가 있다.

그런데 그 후에 인수를 지정할 필요가 있는 옵션에 대해서 필요한 인수를 사용자가 쓰지 않았을 경우 루틴은 어떻게 될까? 혹은 「-ab」 와 같이 사용자가 2개의 옵션을 함께 지정해서 스크립트를 호출했을 경우에는? 그러한 경우에도 인수를 제대로 넘길수 있을까? 스크립트에 옵션이 필요한 경우는 자주 있는 일이므로 쉘스크립트의 옵션을 넘겨야 하는 경우가 자주 생긴다. 옵션을 넘기는 표준적인 방법은 없는 것일까?

옵션을 넘기는 표준적인 방법은 bash 명령어 「getopts」 를 사용하는 것이다. 아래에 getopts의 메뉴얼 페이지에 실려 있는 예를 약간 나타내 본다.

    #!/usr/bin/env bash
    # cookbook filename: getopts_example
    #
    # using getopts
    #
    aflag=
    bflag=
    while getopts 'ab:' OPTION
    do
      case $OPTION in
      a)    aflag=1
            ;;
      b)    bflag=1
            bval="$OPTARG"
            ;;
      ?)    printf "Usage: %s: [-a] [-b value] argsn" $(basename $0) >&2
            exit 2
            ;;
      esac
    done
    shift $(($OPTIND - 1))

    if [ "$aflag" ]
    then
      printf "Option -a specifiedn"
    fi
    if [ "$bflag" ]
    then
      printf 'Option -b "%s" specifiedn' "$bval"
    fi
    printf "Remaining arguments are: %sn" "$*"

이 예에서는 두가지 타입의 옵션이 지원되고 있다. 첫번째 타입의 옵션은 단순하게 단독으로 사용되어 인수가 없는 경우이다. 이 타입의 옵션은 보통 명령어의 동작을 변경하는 플래그가 된다. 예에서는 ls 명령어의 -l 옵션등이 있다. 두번째 타입의 옵션은 인수가 필요한 경우이다. 이 타입의 옵션의 예로는 mysql 명령어의 -u 옵션(「mysql -u sysadmin」등과 같이 사용자명을 지정해야함)이 있다. 그러면 getopts 을 사용해 이 두가지 타입의 옵션을 사용할 수 있는 방법을 알아보자.

 getopts 을 사용하기 위해서는 두개의 인수를 지정해야 한다.
  
 getopts 'ab:' OPTION

첫번째 인수는 옵션으로 사용하는 문자의 리스트이며, 두번쨰 인수는 쉘 변수의 이름이다. 위의 쉘스크립트의 예에서는 유효한 옵션으로 -a 와 -b 만을 정의하고 있으므로 getopts 의 인수에는 그러한 두문자(와 콜론)만이 지정되어 있다. 콜론이 어떤 의미인가 하면 (예를 들어 「-u 사용자명」 이나 「-f 파일명」과 같은 형태로) -b 에 인수가 필요하다라는 것을 말하고 있다. 또한 콜론은 인수를 요구하는 각 옵션 문자의 바로 옆에 지정할 필요가 있다. 따라서 예를 들면 인수를 요구하는 것이 -a 옵션만일 경우에는 a:b 가 된다.

getopts 명령어는 쉘스크립트의 인수 리스트가 넘어왔을때 찾아낸 값($1 이나 $2 등)을 두번쨰 인수로 지정된 변수에 설정한다. 「-」 로 시작하는 인수는 옵션 인수로 간주되어  「-」 를 제외한 문자 부분만 지정한 변수(위의 예에서는 $OPTION)에 대입된다. 그 후 getopts 는 true(0) 을 돌려주므로 while 루프가 옵션을 처리해서 그 후 인수가 없어질 때까지(또는 사용자가 옵션의 종료를 명시적으로 나타내기 위해서 입력한 「--」 를 찾아낼 때까지) getopts 가 반복 수행된다. 마지막에 getopts 가 false(0 이외의 값 ) 을 돌려주면 while 루프는 종료한다.

루프 안에서는 $OPTION 변수에 대해서 case 문장을 이용해서 처리해야 할 옵션 문자가 발견되었을 경우에 플래그를 설정하거나 옵션이 지정되었을 경우에 수행해야 할 동작을 수행한다. 한편 인수를 요구하는 옵션의 경우 그 인수는 쉘 변수 「$OPTARG」 에 설정된다 ( $OPTARG 은 이미 정해진 이름으로 여기서 나오는 변수인 $OPTION 과는 관계없다). 다만 $OPTARG 변수의 값은 while 문으로 루프가 반복 수행되어 getopts 가 수행될때마다 리셋 되므로 다른 변수에 대입해 저장해 둘 필요가 있다.

예에서 case 문의 3번째 패턴은 「?」 로 임의의 한 문자에 매치하는 쉘의 패턴이다. getopts 는 정의된 옵션(여기에서는'ab:') 이외의 옵션을 찾아냈을 경우에는 변수(여기에서는 $OPTION) 안에 리터럴의 「?」 를 대입한다. 따라서 case 문을 ?) 또는 '?') 으로 해서 「?」에 매치 시킬 수도 있지만 이곳에서의 case 문의 디폴트로는 임의의 한 문자에 매치하는 패턴으로 「?」 를 사용해 두면 리터럴의 「?」도 포함한 임의의 한 문자에 매치하므로 편리하다.

사용법 메세지를 표시하는 부분에서는 메뉴얼 페이지에 게재되어 있는 스크립트에 2가지르 더  변경 했다. 첫번쨰 변경점으로 실행시에 사용될 가능성이 있는 여분의 경로명을 모두 없앤 스크립트명을 표시하기 위해 $(basename $0) 을 사용하도록 했다. 두번째 변경점으로는 사용법 메세지를 표준 에러 출력(>&2)에 출력하도록 했다. 알지못하는 옵션이나 인수 부족의 경우를 위해 getopts 의 에러 메세지는 반드시 모두 표준 에러 출력에 출력되므로 사용법 메세지도 거기에 맞추었다.

 while 루프가 종료하면 다음과 같은 행이 실행된다.

    shift $(($OPTIND - 1))

위에서는 shift 문을 사용해서 $1 나 $2 와 같은 쉘스크립트의 위치 파라미터를 주어진 수만큼 앞으로 이동시키고 있다 (즉 앞에 있는 파라미터를 버리고 있다). $OPTIND 변수는 getopts 가 넘길때의 위치를 기억해 두기 위해 사용하는 인수 인덱스다. 따라서 이 shift 문을 실행함으로써 인수를 모두 넘긴후 처리가 끝난 옵션을 모두 버리게 된다. 예로써 아래와 같은 명령어 라인을 생각해 보자.

    myscript -a -b alt plow harvest reap

이 경우 옵션을 모두 넘겼을때에 $OPTIND 은 4 가 되어 있다. ($OPTIND-1) 에 의해 쉬프트가 3번 실행되므로 인수 중에서 옵션 인수만이 버려지게 되어 「echo $* 」 를 실행해 보면 다음과 같은 결과가 된다.

    plow harvest reap

따라서 나머지(옵션이 아니다) 인수를 이용하기 쉬운 상태가 된다. 위의 스크립트에서는 맨 마지막 줄의 printf 로 나머지의 인수를 모두 표시했다.