C언어 실행 시 긴 이름의 옵션 사용 함수 getopt_long()

2020. 3. 16. 10:28 컴퓨터/프로그래밍

C함수 실행 시 긴 이름의 옵션 사용 getopt_long()

프로그램을 실행할 때의 옵션을 처리합니다.

  • 헤더: getopt.h
  • 형태: int getopt_long(int argc, char * const argv[], const char *optstring,
    const struct option *longopts, int *longindex)
  • 인수: int argc 인수의 개수
    char * const argv[] 인수 내용
    const char *optstring 검색하려는 짧은 옵션들의 문자열
    const struct option *longopts 검색하려는 긴 이름의 옵션 모음
    int *longindex 옵션에 해당되는 색인 번호
  • 반환: int 0 == 긴 이름의 옵션
    -1 == 옵션 분석을 모두 마침
    나머지는 getopt()와 동일

설명

getopt_long()은 getopt()처럼 프로그램을 실행할 때의 옵션을 확인합니다. 예를 들어 아래와 같이 프로그램을 실행할 수 있습니다.

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

실행 시 입력한 옵션을 프로그램에서 처리하기 위해서 main()함수의 argc 나 argv를 직접 사용하기에는 여러 모로 불편합니다. 대신에 getopt() 함수를 이용하는 것이 편합니다. 그러나 getopt() 함수로 처리하는 옵션은 -r -c 와 같이 문자 하나만 사용하는 옵션에만 사용할 수 있습니다. 즉, 아래와 같이 긴 이름의 옵션을 사용하지 못합니다.

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

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

caution

실행 옵션 관련 함수

  • getopt() : '-'로 시작하는 짧은 이름의 옵션을 검색합니다.
  • getopt_long() : 짧은 이름의 옵션과 '--'로 시작하는 긴 이름의 옵션을 검색합니다.
  • getopt_long_only() : 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       opt;
   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 ./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()처럼 문자 하나 옵션까지 처리하는 예입니다.

#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
옵션 repeat
옵션 name=test.c
./a.out: invalid option -- 'f'
./a.out: invalid option -- 'x'
./a.out: unrecognized option '--xxx'
var55= 0
var99= 99
non-option ARGV-elements: test3.c
]$


이 댓글을 비밀 댓글로