본문 바로가기

linux

[shell script] getopts - 스크립트 옵션 처리

명령어에는 옵션을 같이 입력받는 경우가 대부분이다

옵션은 보통 POSIX에 명시된 형식에 따라 지정하게 되는데

형식은 다음과 같다

$ cmd -a arg1 -bc arg2 --param-x

사용자는 이 옵션들을 셸 기본 변수인 $@, $*, $1... 등을 통해 받아서 파싱하여 각 옵션에 대응할 수 있도록 처리를 해 주게 된다

문제는 이 때 처리해 주어야 할 경우의 수가 상당히 많다는 것이다

예를들어

  • -bc 처럼 둘 이상의 옵션을 붙여서 사용하는 경우도 있고
  • 옵션값이 붙는 경우도, 아닌 경우도 있고
  • --param와 같은 long 옵션도 있으며, 그 뒤에 -값이 올 때도 있다 (이 때는 옵션이 아니다)

다행히도 getopts라는 유닉스 내장 스크립트가 이러한 파싱을 대신 해준다

참고로 c의 라이브러리인 getopt에서 왔다고 한다

 

 

사용법

getopts optstring var

위의 명령을 셸 스크립트 내에서 사용하면 해당 스크립트에 전달된 옵션 값을 하나 가져오게 된다

하나이므로 실제로는 while과 함께 사용하게 된다

실제 사용법

#!/bin/bash

while getopts "ab:c" opt; do
    case $opt in
        a)
            #a에 대한 옵션
           echo a 옵션 발동! 
            ;;
        b)
            #b에 대한 옵션
            #ex) 옵션에 대한 값은 OPTARG에 자동으로 저장된다
            myvalue=$OPTARG
            ;;
        c)
            #c에 대한 옵션

            ;;
        ?) #에러에 대한 옵션이다
            echo 무언가 잘못됐어..!
            ;;
    esac
done

 

default 값을 주고싶은 경우

error에 대한 경우의 수는 있는데, 옵션이 없을 경우 default값에 대한 처리는 직접 구현해야 하는듯 하다

만약 전달인자가 없으면 OPTIND의 값이 1이므로, 그 점을 이용해서 조건처리하거나 getopts 전에 초기값을 주도록 하자

 

 

 

optstring

optstring은 사용할 옵션들을 일정한 형식으로 나열한 문자열이다

a:bc 와 같은 형식으로 지정되는데, 

이 값에 따라 해당 옵션에 옵션값이 존재하는지 등을 체크하게 되고, 에러를 체크하게 된다

형식은 간단하다

만약 옵션에 대한 옵션값이 있다면 :를 붙여서 "a:"와 같은 형식으로

옵션값이 없다면 "a" 와 같이 나타내면 된다

 

예시

"ab:c:d:h"
> a, h는 옵션값(argument)가 없고 b, c, d는 사용시 값을 지정해 주어야 한다
$ mycmd -ab -c value1 -d value2

 

동작 과정

  • 스크립트 내에서 getopts가 실행되면 넘어온 매개변수 중 순서대로 하나를 받는다(아마도 $*혹은 $@를 통해)
  • 이 때 매개변수가 -하나로 시작할 시 옵션으로 판단하여 optstring 값과 비교한다
  • 만약 optstring내에 해당 변수가 존재하면 해당 변수를 var에 저장하고, 다음 인덱스의 매개변수를 가져와 $OTPARG에 저장된다
  • 만약 다음 옵션이 존재한다면 true를 반환한다

이렇게 한 번의 실행마다 다음 값을 참조한다는 것은 내부에 커서 역할을 하는 개체가 존재한다는 것인데, OPTIND가 그 역할을 한다.

OPTIND는 환경변수로, 1부터 시작하여 한 번 실행될 때마다 1씩 늘어난다

 

 

Verbose Error Checking

옵션을 사용하는 과정에서 여러 경우의 수가 존재할 수 있다

  • optionString에 존재하지 않는 옵션을 사용했다던지
  • argument가 존재해야 하는 옵션인데 없다던지 

getopts는 기본적으로 이런 상황들에 대해 verbose error message를 발생시킨다

(stack tarce와 동일한 그것인듯하다)

이 경우 var에 는 ?이 저장되고, OPTARG에는 값이 저장되지 않고, 에러 메세지를 발생시킨다

따라서 에러에 대한 처리는 case의 ? 분기를 이용하면 된다

 

그럼 다음과 같은 경우는 어떨까

  • getopts과는 별도로 argument를 전달하고 싶다던지

이런 상황에서 에러 메시지를 피하기 위해 Silent error checking mode 를 설정할 수 있다.

방법은 optionString의 제일 처음에 :를 붙이면 Silent 모드로 동작하게 된다  ex) ":ab:c"

이 경우 에러메세지가 발생하지 않고 OPTARG에 : 값이 저장된다

 

 

 

+ getopts를 함수 내에서 사용할 때

쉘 스크립트 파일이 아닌 함수를 실행할 때도 옵션을 사용하고 싶을 때가 있다

하지만 함수 내에 위의 getopts 코드를 작성하면 아마 정상 작동하지 않을 것이다.

다음은 실제로 발생한 현상이다

  • 명령을 처음 실행할 때에는 옵션이 정상 작동하지만, 같은 명령을 다시 입력하면 정상 작동하지 않는다
  • 그 후에 옵션을 하나 추가하면 뒤에 추가한 옵션만 정상 작동한다.
  • 근데 잘못된 옵션을 주거나, 인자 값이 없으면 에러 체크는 된다 (optstring이 파싱된다)

함수 내에서는 getopts를 사용할 수 없는 것일까?

결론부터 말하자면 사용 가능하다.

문제는 OPTIND에 존재한다

에러 없이 사용하려면 OPTIND를 로컬 변수로 선언하던지, 1로 초기화 해주어야 한다

 

왜?

OPTIND는 환경변수로, getopts는 받은 인자의 개수를 파악하고, 내부 카운트 변수를 OPTIND에 대입한다( 다른 값을 강제 대입해도 다시 원래 인덱스로 돌아간다) 

문제는 bash에서 getopts를 사용할 시 로컬로 선언된 OPTIND를 사용하지만, 함수로 사용할 시 전역 변수를 하용한다는 점이다 

(실제로 OPTIND 와, local OPTIND 선언 후의 값을 출력해보면 서로 다르다)

전역변수의 경우 자동으로 1로 초기화되지 않기 때문에 생긴 문제이다

 

 

 

 

 

c의 버전 POSIX의 버전과 LINUX의 버전 등이 조금씩 다르다고 하는데 그건 다음에 알아봐야겠다

 

 

 

'linux' 카테고리의 다른 글

[linux] vi로 파일을 열었는데 @@@@@@만 뜨는 문제  (0) 2022.02.21
[bash] IFS란  (0) 2021.12.08
Vundle 사용 과정 요약  (0) 2021.11.27
vi와 vim (Vundle 사용 관련)  (0) 2021.11.27