C/C++
프로그램을 작성하시다 보면 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언어