프로그램을 작성하시다 보면 gcc 컴파일러처럼 실행할 때 다양한 인수가 필요할 때가 있습니다. 예로 시리얼 통신 프로그램을 작성하신다면,

방법 1: ]$ ./rs232 /dev/ttyS0 -b 115200 -d 8 -p no

이런 식으로 인수를 받으실 수 있을 것입니다. 물론 인수 구분자 없이 차례로 입력하실 수도 있습니다.

방법 2: ]$ ./rs232 /dev/ttyS0 115200 8 n

이런 식으로 말이죠. 두 방법다 장단점이 있습니다. 또한 프로그래머 마다 선호하는 방법이 있습니다만 사용하지 않는다고 하더라도 이런 방법도 있구나하는 것을 알아 두시는 것도 좋으리라 생각됩니다.

방법 1에 비해 방법 2가 더 단순하고 깔끔해 보일 수 있습니다만,

  • 방법 1은 순서에 관계없이 인수를 배열할 수 있습니다.
  • 방법 1은 기본값을 지정하고 변경되는 내용만 인수로 입력할 수 있습니다. 즉, 기본값과 같은 인수를 생략할 수 있습니다.
  • 처리할 인수의 개수가 많을 때에는 방법 2번 보다 방법 1이 혼란 스럽지 않고 더 깔끔하며, 직관적입니다.

이렇게 인수를 처리하기 위해 C 라이브러리 함수 중에 getopt()라는 함수가 있습니다. 이번에 C 라이브러리 함수 게시판에 올렸는데요, 저는 이 함수가 있는 줄도 몰라서 지금까지 인수를 모두 확인하는 코드를 직접 작성했습니다.

getopt()

getopt() 함수를 이용하여 방법 1에 따라 예제를 작성해 보겠습니다. 인수 옵션 문자는 아래와 같습니다.

  • -b : 보오율 지정
  • -t : 데이터 비트 사이즈 지정
  • -s : stop bit 크기 지정
  • -p : 패리티 지정

그리고 옵션 문자없이 입력된 문자열은 통신 포트의 장치명으로 사용하겠습니다.

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

int main( int argc, char **argv)
{
   char *port     = NULL;
   int   baud     = 119200;
   int   bit_size = 8;
   int   parity   = 0;
   int   stop_bit = 1;

   int   param_opt;

   while( -1 !=( param_opt = getopt( argc, argv, "b:t:p:s:")))
   {
      switch( param_opt)
      {
      case  'b'   :  baud     = atoi( optarg);  break;
      case  't'   :  bit_size = atoi( optarg);  break;
      case  's'   :  stop_bit = atoi( optarg);  break;
      case  'p'   :  if      ( 0 == strcmp( "odd" , optarg))   parity  = 1;
                     else if ( 0 == strcmp( "even", optarg))   parity  = 2;
                     else                                      parity  = 0;
                     break;
      }
   }

   // 옵션 문자가 없는 문자열 정보가 있다면
   // 통신 포트 장치명으로 지정한다.

   if (optind < argc)   port  = argv[optind];

   printf( "port  = %sn", port);
   printf( "baud  = %dn", baud);
   printf( "bit size = %dn", bit_size);
   printf( "sto bit  = %dn", stop_bit);

   switch( parity)
   {
   case  1  :  printf( "parity  = oddn");   break;
   case  2  :  printf( "parity  = evenn");  break;
   default  :  printf( "parity  = non");
   }

   return 0;
}

아래는 실행한 모습입니다. 보시면 인수를 생략하면 지정된 기본값이 사용되고 인수의 순서에 관계 없음을 아실 수 있습니다.

]$ ./a.out /dev/ttyS0 -b 38600
port  = /dev/ttyS0
baud  = 38600
bit size = 8    <-- 기본값
sto bit  = 1    <-- 기본값
parity  = no    <-- 기본값

]$ ./a.out /dev/ttyS0 -s 2 -b 38600 
port  = /dev/ttyS0
baud  = 38600
bit size = 8    <-- 기본값
sto bit  = 2
parity  = no    <-- 기본값

]$./a.out /dev/ttyS0 -b 38600 -s 2 -p odd
port  = /dev/ttyS0
baud  = 38600
bit size = 8
sto bit  = 2
parity  = odd

]$

 

물론 이것은 예일 뿐 반드시 이렇게 작성하는 것이 좋다라는 것은 절대 아닙니다. getopt()함수를 이용하여 간단하게 인수를 처리할 수 있지만 저 같은 경우에는 아직도 아래와 같이 사용하는 것을 좋아합니다.

]$ ./rs232 /dev/ttyS0 -baud=9600 -databit=8 -stopbit=2 -parity=odd -comnumber=1

시리얼 포트의 장치명은 반드시 들어 가기 때문에 첫번째 인수에 고정했습니다. 그리고 나머지는 -옵션명을 문자가 아닌 문자열을 사용하고 있습니다.

순전히 저 개인적인 생각입니다만 프로그램 코딩 방법에는 여러가지가 있으므로 반드시 이 방법이 좋다라는 코드는 없다고 생각됩니다. 잛게 말씀드린다면 적재적소라고 말씀드리고 싶습니다. ^^

때로는 프로그램 인수가 문자만 있고 내용이 필요 없는 경우에서는 getopt()가 편리합니다. 즉, 아래와 같은 경우죠.

]$ ./a.out -t -a

]$ ./a.out -t -a -3

]$ ./a.out -t -a -3 -f filename

또는 여러 가지 방법을 섞는 방법도 있습니다.

]$ ./rs232 /dev/ttyS0 -baud=9600 -databit=8 -s 2 -p odd -comnumber=1

위 방법은 -p 로 옵션 문자열을 사용하면서 getopt()함수를 사용하는 방법과 -baud=9600을 사용하여 문자열을 직접 검색하는 방버을 함께 사용한 것입니다. 즉, getopt()를 사용해서 인수를 구할 것은 구하고 나머지는 직접 문자열을 검색해서 처리한다는 것이지요.

그러나 -s 처럼 문자로 인수를 처리하는 것도 다음 부가 정보와 공백없이 붙여 쓸 수 있기 때문에 조심하셔야 합니다.

즉,

]$ ./rs232 /dev/ttyS0 -baud=9600 -databit=8 -s 2 -p odd -comnumber=1

이것과

]$ ./rs232 /dev/ttyS0 -baud=9600 -databit=8 -s2 -p odd -comnumber=1

이것은 같은 방법으로 처리가 됩니다.

-s 다음에 오는 옵션 문자열이 공백 없이 붙여 쓸 수 있다는 것이죠. 그러므로 -s2= 과 같은 문자열 옵션이 있다면 실행에 오류가 발생할 수 있습니다.

]$ ./rs232 /dev/ttyS0 -baud=9600 -databit=8 -s2=howto -p odd -comnumber=1

(1) 인수 -s2는 옵션 s2 로 처리될 수 있습니다만 (2) getopt()에도 걸려서 -s 옵션에 대해 2=howto 문자열이 지정되됩니다.

 

태그: *C언어