설명

프로그램을 실행할 때, 옵션을 지정할 수 있습니다. 예를 들어 아래와 같이 프로그램을 실행할 수 있습니다.

 

]$ ./sample-app -r -c -f test.txt

 

실행 시 입력한 옵션을 프로그램에서 처리하기 위해서 main()함수의 argc 나 argv를 직접 사용하기에는 여러 모로 불편합니다. 대신에 getopt() 함수를 이용하는 것이 편합니다.

 

그러나 getopt() 함수로 처리하는 옵션은 -r -c 와 같이 문자 하나만 사용하는 옵션에만 사용할 수 있습니다. 즉, 아래와 같이 긴 이름의 옵션을 사용하지 못합니다.

 

]$ ./sample-app --repeat --filename=test.txt

 

옵션이라도 문자 하나 보다는 긴 이름의 옵션을 사용하는 것이 편할 것입니다. 이런 긴 이름의 옵션을 쉽게 사용할 수 있도록 해주는 함수가 getopt_long() 함수가 되겠습니다.

getopt()와 다른 점, struct option

getopt()를 사용하는 옵션은 - 문자 하나만 사용합니다. 그러나 getopt_long()를 사용하는 긴 옵션은 -- 처럼 문자 두개로 입력해야 합니다.

 

getopt_long() 함수를 사용하기 위해서는 getopt() 함수와 달리 긴 이름의 옵션 정보를 미디 준비해야 합니다. 예를 들어,

  • 옵션에 --opt55를 입력했다면 var55 변수에 55라는 값을 구하고, 옵션에 없다면 0의 값을 가지게 하겠습니다.
  • 옵션에 --opt99를 입력했다면 var99 변수에 99라는 값을 구하고, 옵션에 없다면 0의 값을 가지게 하겠습니다.
  • --repeat 옵션이 있는지 여부를 확인 하겠습니다.
  • --name= 옵션을 사용했다면 옵션에 지정한 문자열을 구하겠습니다.

이와 같이 처리해야할 옵션이 결정되었다면 아래와 같이 struct option을 작성합니다.

    struct option options[] = { 
        {"opt55" , 0, &var55, 55}, 
        {"opt99" , 0, &var99, 99}, 
        {"repeat", 0, 0, 0}, 
        {"name"  , 1, 0, 0}, 
        {0, 0, 0, 0} 
    };

struct option은 아래와 같이 구성되어 있습니다.

struct option
{
  const char *name; // 옵션의 긴 이름입니다.
  int has_arg; // 옵션에 해당하는 자료, 즉 --name=test.c 처럼 인수가 필요한지
// 여부를 지정합니다. // 0 : 필요 없음 // 1 : 필요 함 int *flag; // 옵션이 입력되었다면 지정한 값을 받을 수 있는 변수 주소입니다. int val; // 옵션이 입력되었다면 여기에 지정한 값을 flag 변수에 대입합니다. };

struct opton 구조에 따라 각 행의 뜻하는 바를 보겠습니다.

  • {"opt55" , 0, &var55, 55}
    옵션의 긴 이름은 opt55 이며,
    옵션에 필요한 인자는 없습니다. 즉, --opt55=어쩌구저쩌구 라고 "=어쩌구저쩌구"를 넣을 수 없습니다.
    옵션이 지정되었다면 var55 변수에 55값을 넣어라가 되겠습니다.
  • {"opt99" , 0, &var9955, 995}
    옵션의 긴 이름은 opt99 이며,
    옵션에 필요한 인자는 없습니다.
    옵션이 지정되었다면 var99 변수에 99값을 대입하게 됩니다.
  •  {"repeat", 0, 0, 0}
    옵션의 긴 이름은 repeat 이며,
    필요한 추가 자료는 없습니다.
    옵션이 있는지의 여부를 받는 포인터 변수도 없습니다.
  • {"name" , 1, 0, 0}
    옵션의 긴 이름은 name이며,
    =로 지정하는 자료가 필요합니다. 없으면 에러 메시지가 출력됩니다.
    옵션이 있는지의 여부를 받는 포인터 변수가 없습니다.

실행 여부에 따라 아래와 같은 결과를 얻을 수 있습니다.

 

]$ ./sample  --opt55 --repeat -name=test.txt

 

  • var99 변수 값에는 변동 없음
  • var55 변수에 55를 대입
  • repeat 옵션이 지정되었음을 알게됨
  • name의 문자열 데이터는 "test.txt"

함수 사용 예

프로그램 작성 예입니다.

#include <stdio.h>
#include <getopt.h>

static int var55;
static int var99;

int main( int argc, char **argv)
{
    struct option options[] = {
        {"opt55" , 0, &var55, 55},
        {"opt99" , 0, &var99, 99},
        {"repeat", 0, 0, 0},
        {"name"  , 1, 0, 0},
        {0, 0, 0, 0}
    };
    char   *name;
    int     index       = 0;

    while( 1)
    {
        opt = getopt_long( argc, argv, "", options, &index);

        if  ( -1 == opt)  break;  // 모든 옵션을 확인했으면 루프 종료

        switch( opt)
        {
        case 0:
            if ( options[index].flag != 0)  break; // 변수 주소 지정이 없다면
                                                   // 다음 옵션을 확인합니다.
                                                   // 즉, --repeat와 --name이 아니면
                                                   // 스킵합니다.

            printf("옵션 %s", options[index].name);

            if ( NULL != optarg)     printf("=%s", optarg);

            printf("\n");
            break;
        } /* switch 의 끝*/
    } /* while 의 끝 */

    printf( "var55= %d\n", var55);
    printf( "var99= %d\n", var99);



    /* 남아있는 코맨드 라인 인수들을( 옵션이 없는) 출력하라. */
    if (optind < argc)
    {
        printf ("non-option ARGV-elements: ");
        while (optind < argc)
        printf ("%s ", argv[optind++]);
        putchar ('\n');
    }
}

실행하면 아래와 같습니다.

 

]$ ./a.out --opt55 --name=test.c --repeat
옵션 name=test.c
옵션 repeat
var55= 55
var99= 0

getopt()와 같은 사용법

또한 getopt() 와 같이 문자 하나 옵션도 사용할 수 있습니다. 이 옵션 사용은 getopt()와 같습니다. 즉, 아래와 같이 getopt_long() 함수를 호출하면 -r, -f 옵션을 사용할 수 있습니다.

opt = getopt_long( argc, argv, "rf:", options, &index);

 

아래의 함수 설명 예에서는 getopt() 함수처럼 문자 하나 옵션까지 처리하는 예를 올렸습니다.

 

헤더 getopt.h
형태 int getopt(int argc, char * const argv[], const char *optstring);
인수 위의 설명을 참고
반환
-1 옵션 분석을 모두 마침
0 긴 이름의 옵션에 대한 처리 필요
나머지 getopt() 함수와 동일

예제

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <getopt.h>

#define TRUE        1
#define FALSE       0


static int var55;
static int var99;

int main( int argc, char **argv)
{
    struct option options[] = {
        {"opt55" , 0, &var55, 55},
        {"opt99" , 0, &var99, 99},
        {"repeat", 0, 0, 0},
        {"name"  , 1, 0, 0},
        {0, 0, 0, 0}
    };
    char   *name        = NULL;
    int     is_repeat   = FALSE;
    int     index       = 0;
    int     opt;

    while( 1)
    {
        opt = getopt_long( argc, argv, "rf:", options, &index);

        if  ( -1 == opt)  break;  // 모든 옵션을 확인했으면 루프 종료

        switch( opt)
        {
        case 0:
            switch( index)
            {
            case 2  : // options[2]에 해당하는 repeat 라면
                is_repeat = TRUE;
                break;
            case 3  : // options[3]에 해당하는 name 이라면
                if ( NULL != optarg) // 인수가 있다면
                {
                    name = malloc( strlen( optarg)+1);
                    memcpy( name, optarg, strlen(optarg)+1);
                }
                break;
            } // switch index
            break;
        case 'r':
            printf("-r 옵션을 사용\n", optarg);
            break;
        case 'f':
            printf("-f 옵션에 대한 값 지정은 %s 입니다.\n", optarg);
            break;
        case '?':
            // 이 처리는 하지 않아도 getopt_long()에서 처리합니다.
            // 즉, 생략해도 됩니다.
            break;
        } // switch opt
    } // while

    printf( "var55= %d\n", var55);
    printf( "var99= %d\n", var99);
    if ( is_repeat)     printf( "repeat 옵션이 있습니다.\n");
    else                printf( "repeat 옵션이 없습니다.\n");

    if ( NULL != name)  printf( "name은 %s 입니다.\n", name);
}
]$ ./a.out --opt99 --repeat --name=test.c -f test3.c -x --xxx
-f 옵션에 대한 값 지정은 test3.c 입니다.
./a.out: invalid option -- x
./a.out: unrecognized option `--xxx'
var55= 0
var99= 99
repeat 옵션이 있습니다.
name은 test.c 입니다.
]$