programing

getopt는 선택적 인수를 매개 변수로 해석하지 않습니다.

firstcheck 2022. 7. 9. 09:21
반응형

getopt는 선택적 인수를 매개 변수로 해석하지 않습니다.

C에서 getopt_long은 명령줄 파라미터에 대한 옵션의 인수를 해석하지 않습니다.

프로그램을 실행하면 다음 실행 예시와 같이 선택적 인수가 인식되지 않습니다.

$ ./respond --praise John
Kudos to John
$ ./respond --blame John
You suck !
$ ./respond --blame
You suck !

테스트 코드입니다.

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

int main(int argc, char ** argv )
{
    int getopt_ret, option_index;
    static struct option long_options[] = {
               {"praise",  required_argument, 0, 'p'},
               {"blame",  optional_argument, 0, 'b'},
               {0, 0, 0, 0}       };
    while (1) {
        getopt_ret = getopt_long( argc, argv, "p:b::",
                                  long_options,  &option_index);
        if (getopt_ret == -1) break;

        switch(getopt_ret)
        {
            case 0: break;
            case 'p':
                printf("Kudos to %s\n", optarg); break;
            case 'b':
                printf("You suck ");
                if (optarg)
                    printf (", %s!\n", optarg);
                else
                    printf ("!\n", optarg);
                break;
            case '?':
                printf("Unknown option\n"); break;
        }
    } 
    return 0;
}

glibc 설명서 또는 getopt man 페이지에는 언급되지 않지만 긴 스타일의 명령줄 매개 변수를 위한 선택적 인수에는 '부호'(=)가 필요합니다.선택적 인수와 매개 변수를 구분하는 공간은 작동하지 않습니다.

테스트 코드를 사용하여 실행하는 예:

$ ./respond --praise John
Kudos to John
$ ./respond --praise=John
Kudos to John
$ ./respond --blame John
You suck !
$ ./respond --blame=John
You suck , John!

man 페이지는 확실히 잘 문서화되어 있지 않지만, 소스코드가 조금 도움이 됩니다.

간단히: 다음과 같은 작업을 수행해야 합니다(단, 이는 좀 지나친 정신지체일 수 있습니다).

if(   !optarg
   && optind < argc // make sure optind is valid
   && NULL != argv[optind] // make sure it's not a null string
   && '\0' != argv[optind][0] // ... or an empty string
   && '-' != argv[optind][0] // ... or another option
  ) {
  // update optind so the next getopt_long invocation skips argv[optind]
  my_optarg = argv[optind++];
}
/* ... */

_getopt_internal 앞의 코멘트 중:

...

ifgetopt다른 옵션 문자를 찾으면 해당 문자가 반환되고 업데이트nextchar 을 할 수 getopt는, 다음의 또는 를 할 수 .

, 「」를 참조해 주세요.getopt1을 반환합니다. ★★★★★★★★★★★★★★★.optindARGV의 ARGV의 ARGV의 ARGV의 ARGV의 ARGV의 의 Elements (가 마지막에.)(ARGV 요소는 옵션이 아닌 요소가 마지막에 오도록 배열되어 있습니다). <-- a note from me: if the 3rd argument to getopt_long starts with a dash, argv will not be permuted

...

콜론이 붙어 있는 ARGV 또는 는 OPTSTRING으로 됩니다.optarg. arg를 로 하는 ARGV로 반환됩니다.현재 ARGV 요소에 텍스트가 있는 경우 다음 형식으로 반환됩니다.optarg그렇지 않으면 0으로 설정됩니다.

...

행간을 좀 읽으셔야 하지만요다음은 사용자가 원하는 작업을 수행합니다.

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

int main(int argc, char* argv[] ) {
  int getopt_ret;
  int option_index;
  static struct option long_options[] = {
      {"praise",  required_argument, 0, 'p'}
    , {"blame",  optional_argument, 0, 'b'}
    , {0, 0, 0, 0}
  };

  while( -1 != ( getopt_ret = getopt_long(  argc
                                          , argv
                                          , "p:b::"
                                          , long_options
                                          , &option_index) ) ) {
    const char *tmp_optarg = optarg;
    switch( getopt_ret ) {
      case 0: break;
      case 1:
        // handle non-option arguments here if you put a `-`
        // at the beginning of getopt_long's 3rd argument
        break;
      case 'p':
        printf("Kudos to %s\n", optarg); break;
      case 'b':
        if(   !optarg
           && NULL != argv[optind]
           && '-' != argv[optind][0] ) {
          // This is what makes it work; if `optarg` isn't set
          // and argv[optind] doesn't look like another option,
          // then assume it's our parameter and overtly modify optind
          // to compensate.
          //
          // I'm not terribly fond of how this is done in the getopt
          // API, but if you look at the man page it documents the
          // existence of `optarg`, `optind`, etc, and they're
          // not marked const -- implying they expect and intend you
          // to modify them if needed.
          tmp_optarg = argv[optind++];
        }
        printf( "You suck" );
        if (tmp_optarg) {
          printf (", %s!\n", tmp_optarg);
        } else {
          printf ("!\n");
        }
        break;
      case '?':
        printf("Unknown option\n");
        break;
      default:
        printf( "Unknown: getopt_ret == %d\n", getopt_ret );
        break;
    }
  }
  return 0;
}

나는 최근에 이 문제를 직접 접했다.Brian Vandenberg와 Haystack이 제안한 것과 유사한 해결책을 찾았습니다.그러나 가독성을 향상시키고 코드 중복을 피하기 위해 다음과 같은 매크로로 모든 내용을 정리할 수 있습니다.

#define OPTIONAL_ARGUMENT_IS_PRESENT \
    ((optarg == NULL && optind < argc && argv[optind][0] != '-') \
     ? (bool) (optarg = argv[optind++]) \
     : (optarg != NULL))

매크로는 다음과 같이 사용할 수 있습니다.

case 'o': // option with optional argument
    if (OPTIONAL_ARGUMENT_IS_PRESENT)
    {
        // Handle is present
    }
    else
    {
        // Handle is not present
    }
    break;

이 솔루션의 구조에 대한 자세한 내용은 블로그 투고(https://cfengine.com/blog/2021/optional-arguments-with-getopt-long/를 참조해 주십시오.

이 솔루션은 테스트되고 있으며, 현재 CFEngine에서 사용되고 있습니다.

저도 같은 문제에 부딪혀 왔어요.그리고 나는 이것을 깨달았다."optional_argument"의 사용 사례는 많지 않습니다.옵션이 필요한 경우 프로그램 로직에서 체크하고 옵션이 옵션인 경우 getopt 수준에서는 모든 옵션이 옵션이기 때문에 아무것도 할 필요가 없습니다.따라서 "optional_argument"의 사용 사례는 없습니다.이게 도움이 됐으면 좋겠다.

ps: 위의 예에서 올바른 옵션은 --syslog --syslog-name "name" --syslog --syslog-name "name" 입니다.

언급URL : https://stackoverflow.com/questions/1052746/getopt-does-not-parse-optional-arguments-to-parameters

반응형