[C] Windows 에서 getopt 구현하기

Linux 에서 명령 인수를 처리하기위한 함수 getopt 를 아주 많이 사용합니다. Windows에서는 그런 함수가 없어서 만들어서 사용해 보도록 하겠습니다.
이글에서는 Windows 에서 getopt 구현 하여 그 코드와 예제를 보여드리도록 하겠습니다.

Linux 에서 getopt 함수란

Linux 에서 getopt 함수는 명령행 인수를 처리하는 데 유용한 함수입니다. 이 함수는 주로 프로그램이 실행될 때 전달된 명령행 인수(argument)를 파싱하고 처리하는 데 사용됩니다. getopt 함수를 사용하면 효율적으로 명령행 옵션을 처리하고 관련된 작업을 수행할 수 있습니다.

getopt 함수는 <unistd.h> 헤더 파일에 선언되어 있습니다. getopt 함수의 프로토타입은 다음과 같습니다:

int getopt(int argc, char * const argv[], const char *optstring);

이 함수는 세 개의 인수를 받습니다:

  1. argc: 명령행에 전달된 인수의 수를 나타내는 정수입니다.
  2. argv: 명령행에 전달된 실제 인수들을 포함하는 문자열 배열입니다.
  3. optstring: 인수로 받을 옵션 문자열을 지정하는데 사용됩니다.

getopt 함수는 getopt_long 함수와 함께 사용될 수 있으며, getopt_long 함수는 더 유연한 옵션 처리를 제공합니다. 그러나 기본적인 옵션 처리에 대해 설명하기 위해서는 getopt 함수에 초점을 맞추도록 하겠습니다.

일반적으로 C 프로그램에서 getopt 함수를 사용하는 방법은 다음과 같습니다:

  1. getopt 함수를 사용하기 전에 extern char *optarg;extern int optind, opterr, optopt; 선언을 추가합니다.
  2. getopt 함수를 반복문 안에서 호출하여 명령행 인수를 처리합니다.
  3. 각 옵션에 대한 처리를 switch 문이나 if 문을 사용하여 수행합니다.
  4. 명령행에 지정된 옵션이 없을 때까지 getopt 함수를 호출합니다.

간단한 예제를 통해 이해를 돕겠습니다. 다음은 프로그램 이름을 포함한 간단한 명령행 인수를 처리하는 C 프로그램의 예제입니다:

#include <stdio.h>
#include <unistd.h>

int main(int argc, char *argv[]) {
    int opt;

    while ((opt = getopt(argc, argv, "abc:")) != -1) {
        switch (opt) {
            case 'a':
                printf("Option 'a' was specified\n");
                break;
            case 'b':
                printf("Option 'b' was specified\n");
                break;
            case 'c':
                printf("Option 'c' was specified with value '%s'\n", optarg);
                break;
            case '?':
                fprintf(stderr, "Unknown option: '%c'\n", optopt);
                break;
        }
    }

    return 0;
}

위 프로그램은 ‘a’, ‘b’, ‘c’ 세 가지 옵션을 인식합니다. ‘c’ 옵션은 추가적인 인수를 가질 수 있으며, optarg 변수를 통해 해당 값을 받아옵니다. 이 프로그램은 다음과 같은 명령행을 처리할 수 있습니다:

./program -a -b -c value

이렇게 하면 각 옵션에 대한 메시지가 출력되고, ‘c’ 옵션의 값은 ‘value’가 됩니다.

이러한 방식으로 getopt 함수를 사용하여 명령행 인수를 처리할 수 있습니다.

Windows 에서 getopt 함수 구현하기

Linux 에서 getopt 를 사용해서 명령인자를 처리를 하던 개발자라면 윈도우즈에서 명령 처리가 리눅스의 그것보다 쉽지 않다는것을 알 수 있습니다.
자체적으로 windows 에는 getopt 함수가 없기 대문입니다.

자 그러면 만들어야지요.

아래 코드는 C 언어로 작성된 getopt 함수의 구현입니다. 이 함수는 명령행 인수(argument)를 파싱하고 처리하는 데 사용됩니다.

int opterr = 1;     // 오류 메시지 출력 여부
int optind = 1;     // 부모 argv 벡터의 인덱스
int optopt;         // 유효성을 확인하는 문자
int optreset;       // getopt 재설정
char *optarg;       // 옵션과 관련된 인수
char EMSG[] = "";

#define BADCH   (int)'?'
#define BADARG  (int)':'

/*
 * getopt -- argc/argv 인수 벡터를 구문 분석합니다.
 */
int getopt(int nargc, char *const *nargv, const char *ostr) {
    static char *place = EMSG;     // 옵션 문자 처리
    char *oli;                      // 옵션 문자 리스트 인덱스

    // 옵션 처리 재설정 또는 스캔 포인터 업데이트
    if (optreset || !*place) {
        optreset = 0;
        // optind가 nargc를 넘거나 현재 위치의 문자가 '-'가 아닌 경우
        if (optind >= nargc || *(place = nargv[optind]) != '-') {
            place = EMSG;
            return (-1);
        }
        // "--"를 찾은 경우
        if (place[1] && *++place == '-') {
            ++optind;
            place = EMSG;
            return (-1);
        }
    }
    // 옵션 문자 확인
    if ((optopt = (int)*place++) == (int)':' || !(oli = (char *)strchr(ostr, optopt))) {
        // 사용자가 '-'를 옵션으로 지정하지 않은 경우, -1로 가정
        if (optopt == (int)'-')
            return (-1);
        if (!*place)
            ++optind;
        // 옵션에 대한 오류 메시지 출력
        if (opterr && *ostr != ':')
            fprintf(stderr, "getopt: illegal option -- %c\n", optopt);
        return (BADCH);
    }
    // 인수가 필요하지 않은 경우
    if (*++oli != ':') {
        optarg = NULL;
        if (!*place)
            ++optind;
    } else {
        // 인수가 필요한 경우
        if (*place)
            optarg = place;
        else if (nargc <= ++optind) {
            place = EMSG;
            if (*ostr == ':')
                return (BADARG);
            if (opterr)
                fprintf(stderr, "getopt: option requires an argument -- %c\n", optopt);
            return (BADCH);
        } else
            optarg = nargv[optind];
        place = EMSG;
        ++optind;
    }
    return (optopt);
}

전체 예제

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

int opterr = 1;     // 오류 메시지 출력 여부
int optind = 1;     // 부모 argv 벡터의 인덱스
int optopt;         // 유효성을 확인하는 문자
int optreset;       // getopt 재설정
char *optarg;       // 옵션과 관련된 인수
char EMSG[] = "";

#define BADCH   (int)'?'
#define BADARG  (int)':'

/*
 * getopt --
 *      argc/argv 인수 벡터를 구문 분석합니다.
 */
int getopt(int nargc, char *const *nargv, const char *ostr)
{
    static char *place = EMSG;     // 옵션 문자 처리
    char *oli;                      // 옵션 문자 리스트 인덱스

    // 옵션 처리 재설정 또는 스캔 포인터 업데이트
    if (optreset || !*place)
    {
        optreset = 0;
        // optind가 nargc를 넘거나 현재 위치의 문자가 '-'가 아닌 경우
        if (optind >= nargc || *(place = nargv[optind]) != '-')
        {
            place = EMSG;
            return (-1);
        }
        // "--"를 찾은 경우
        if (place[1] && *++place == '-')
        {
            ++optind;
            place = EMSG;
            return (-1);
        }
    }
    // 옵션 문자 확인
    if ((optopt = (int)*place++) == (int)':' || !(oli = (char *)strchr(ostr, optopt)))
    {
        // 사용자가 '-'를 옵션으로 지정하지 않은 경우, -1로 가정
        if (optopt == (int)'-')
            return (-1);
        if (!*place)
            ++optind;
        // 옵션에 대한 오류 메시지 출력
        if (opterr && *ostr != ':')
            fprintf(stderr, "getopt: illegal option -- %c\n", optopt);
        return (BADCH);
    }
    // 인수가 필요하지 않은 경우
    if (*++oli != ':')
    {
        optarg = NULL;
        if (!*place)
            ++optind;
    }
    else
    {
        // 인수가 필요한 경우
        if (*place)
            optarg = place;
        else if (nargc <= ++optind)
        {
            place = EMSG;
            if (*ostr == ':')
                return (BADARG);
            if (opterr)
                fprintf(stderr, "getopt: option requires an argument -- %c\n", optopt);
            return (BADCH);
        }
        else
            optarg = nargv[optind];
        place = EMSG;
        ++optind;
    }
    return (optopt);
}


int main(int argc, char *argv[])
{
    int opt;

    while ((opt = getopt(argc, argv, "abc:")) != -1)
    {
        switch (opt)
        {
        case 'a':
            printf("Option 'a' was specified\n");
            break;
        case 'b':
            printf("Option 'b' was specified\n");
            break;
        case 'c':
            printf("Option 'c' was specified with value '%s'\n", optarg);
            break;
        case '?':
            fprintf(stderr, "Unknown option: '%c'\n", optopt);
            break;
        }
    }

    return 0;
}

빌드된 위 프로그램을 cmd 로 실행 시켜보도록 하겠습니다. 출력은 다음과 같습니다.

D:\>program -a -b -c value
Option 'a' was specified
Option 'b' was specified
Option 'c' was specified with value 'value'

결론

Windows 에서 getopt 함수를 만들어 그 코드와 에제를 보여드렸습니다.
위 코드로 windows 에서도 getopt를 쉽게 사용해 보시기 바랍니다.

Leave a Comment