21.3.2. POSIX getopt() 기반 커맨드라인 파서(Parser) 구현
NSH 셸이 땀 흘려 가공해 준 argc와 argv[] 배열이 메인 함수의 통로를 통과해 우리 코드의 손아귀에 들어왔다. 이제 모듈 개발자로서 스레드를 띄우기 전에 가장 먼저 해야 할 일은, **“사용자가 나한테 무슨 명령(Command)과 무슨 스위치(Option)를 던졌는가?”**를 해석하는 것이다.
초보자들은 흔히 이 작업을 위해 구시대적인 if (strcmp(argv[1], "start") == 0) 같은 하드코딩된 콜백 분기문들을 수십 줄씩 늘어놓곤 한다. 하지만 PX4 코어 개발자들은 이런 난잡한 스파게티 코드를 극도로 경멸하며, 대신 유닉스(UNIX) 천재 연금술사들이 만들어둔 범용 파싱의 단일 기법인 POSIX getopt() 함수의 사용을 강력하게 권장한다(사실상 강제한다).
1. getopt() 라이브러리의 본질
getopt()는 C 라이브러리(<unistd.h>)에 내장된 아주 강력하고 우아한 커맨드라인 토큰 분석기이다.
이 함수는 픽스호크 콘솔에 입력된 긴 배열(argv)을 차례대로 하나씩 삼키면서, 우리가 미리 지정해둔 “합법적인 알파벳 플래그(예: -i, -f)“가 등장하면 이를 쏙쏙 뽑아내고 그 뒤에 딸려온 옵션 값(예: 5)까지 안전하게 분리해 주는 역할을 수행한다.
2. PX4 표준 파서 아키텍처 (Boilerplate)
어떤 PX4 기본 모듈(예: sensors, commander, ekf2)의 소스 코드를 열어봐도, 그 메인 함수 내부의 파싱 로직은 거의 복붙(Copy & Paste)을 한 것처럼 완벽하게 동일한 구조를 띠고 있다. 이것이 바로 PX4 펌웨어의 단결된 코드 스타일(Code Style)이다.
가장 전형적인 getopt() 파싱 템플릿의 뼈대는 다음과 같이 생겼다.
#include <unistd.h> // getopt 함수가 들어있는 헤더
int custom_app_main(int argc, char *argv[])
{
int ch;
int my_custom_value = 0; // 옵션 값을 저장할 변수
// 1. 파서 초기화 (NSH 특성상 필수)
int myoptind = 1;
const char *myoptarg = nullptr;
// 2. 루프를 돌며 알파벳 토큰 수집
while ((ch = px4_getopt(argc, argv, "v:f", &myoptind, &myoptarg)) != EOF) {
switch (ch) {
case 'v':
my_custom_value = atoi(myoptarg); // -v 뒤의 숫자를 저장
break;
case 'f':
// -f 플래그가 주어졌을 때의 처리
break;
case '?':
default:
// 알 수 없는 플래그일 때 사용법 출력 (Usage)
custom_app_usage("Unrecognized option");
return 1;
}
}
// 3. 남은 명령어(예: start, stop) 처리
if (myoptind >= argc) {
custom_app_usage("Missing command");
return 1;
}
const char *verb = argv[myoptind]; // "start" 또는 "stop" 토큰 추출
if (!strcmp(verb, "start")) {
// 모듈 시작 로직 호출...
} else if (!strcmp(verb, "stop")) {
// 모듈 중지 로직 호출...
}
// ...
위의 뼈대 코드에는 픽스호크 환경만이 가지는 몇 가지 특이점이 숨어있다. 일반 C 라이브러리의 getopt가 아니라 **px4_getopt**라는 자체 래퍼(Wrapper) 함수를 사용한다는 점, 그리고 글로벌 변수인 optind를 쓰지 않고 지역 변수(myoptind)를 포인터로 넘긴다는 점이다.
왜 굳이 표준 함수를 버리고 이렇게 복잡한 래퍼(Wrapper)를 써야만 하는 것일까? 그리고 "v:f"라는 저 암호 같은 포맷 스트링(Format String)은 컴파일러에게 어떤 지시를 내리는 것일까? 이어지는 단원(21.3.2.1 하위)들에서 이 파싱 루프의 톱니바퀴 조각들을 현미경으로 하나하나 들여다보겠다.