유틸리티 규약, getopt, gettext - 윈디하나의 솔라나라

목차

개요

유틸리티 규약

강제성은 없으며, 프로그래머를 위한 지침일 뿐이지만, 이 규약을 알고 있다면, 규약을 지킨 유틸리티를 더 쉽게 사용할 수 있다. 이 규약이 만들어지기 이전부터 있던 솔라리스에 번들된 유틸리티는 이 규약을 지키지 않은 것도 있다.
  1. 유틸리티의 이름은 2자 이상 9자 이하이어야 한다.
  2. 유틸리티의 이름은 소문자를 포함하고 있어야 한다.
  3. 각각의 옵션명은 영문/숫자로 1자이어야 한다. -W는 벤더별 옵션으로 사용한다. 1)
  4. 모든 옵션은 대시(-)로 시작한다.
  5. 옵션인자가 없는 옵션은 -으로 그룹화 해서 사용할 수 있다. (예를 들어 ls -a -lls -al이 같다)
  6. 각 옵션과 옵션인자는 공백문자로 구분되어야 한다.
  7. 옵션인자(option arguments, 옵션의 인자, cc -o abc abc.c에서, abc 가 -o 옵션의 옵션인자에 해당된다)는 옵션을 사용한다면 반드시 입력해야 한다.
  8. 옵션에 대해 두개 이상의 옵션인자가 필요하다면 각 옵션인자는 콤마(,)나 공백 문자( )로 옵션 안에서 구분할 수 있도록 해야 한다.
  9. 모든 옵션은 명령라인에서 피연산자(operand) 앞에 사용되어야 한다.
  10. -- 는 옵션의 끝을 의미한다. -- 뒤에 나오는 -로 시작하는 오퍼랜드를 더이상 옵션으로 인식하지 않는다. (ls -l -- -a명령은 -a라는 파일을 -l 형식으로 출력하라는 의미다)
  11. 옵션의 순서는 상관 없이 정하지만, 옵션인자의 입력은 프로그램의 특성상 순서가 중요할 수 있다. 먼저 입력한 옵션인자를 먼저 처리한다. (make -f makefile1.mk -f makefile2.mk all와 같은 명령에서 makefile1.mkmakefile2.mk보다 먼저 처리해야 한다는 의미다)
  12. 오퍼랜드의 순서에 대한 의미는 프로그램의 특성을 따른다. (cp a b명령은 오퍼랜드의 순서가 중요하다)
  13. 파일을 의미하는 오퍼랜드 또는 옵션인자에서 -stdin 또는 stdout을 의미한다.
1) 같은 유틸리티를 다른 유닉스에 포팅할 때, 특정한 유닉스를 위한 전용 옵션은 -W으로 시작하라는 의미다. 비슷한 의미를 가진 옵션으로 -X가 있는데 실험적 옵션 또는 비 표준 옵션을 의미한다.

이 규약을 지키기 위해, 프로그래머를 위해 만들어 놓은 함수가 getopt(3C) 이다.

유틸리티 규약 예

커맨드 [-a] [-b] [-c 옵션인자1] [-d|-e] [-f옵션인자2] -g 옵션인자3 [오퍼랜드...]
위 문법은 아래와 같이 표현할 수도 있다. 실제로 -a -b대신 -ab로 실행해도 동일하게 작동한다.
커맨드 [-ab] [-c 옵션인자] [-d|-e] [-f옵션인자] -g 옵션인자3 [오퍼랜드...]

getopt

많은 프로그램들이 옵션을 처리하기 위해서 getopt(3C)를 사용하고 있을 것이다. 솔라리스의 getopt 는 GNU getopt 의 형식을 많이 따르고 있다.
getopt.c
(1,659 바이트)
/*
	getopt sample
	WindyHana's Solanara http://www.solanara.net/
	cc -o getopt getopt.c
*/
#include <ctype.h>
#include <stdio.h>
#include <unistd.h>

void printusage() {
	printf("\n");
	printf("getopt 예제\n");
	printf("\n");
	printf("사용법: getopt [-abh] [-f [파일명] [-o 파일명] 인자1 [인자2 ...]\n");
	printf("\n");
}

int main(int argc, char *argv[]) {
	int c;
	int aflg = 0, bflg = 0, errflg = 0;
	char *ifile = "NONE";
	char *ofile = "NONE";
	extern char *optarg;
	extern int optind, optopt;
	/*
		※ 짧은 버전: [-abh] [-f [파일명] [-o 파일명] 인자1 인자2
		※ getopt()의 세번째 인자의 첫번째 단어가 : 이어야
			- case ':'에서 정한 구문이 실행되고
			- 기본 오류 메시지(option requires an argument, illegal option)을 보이지 않음
	*/
	while ((c = getopt(argc, argv, ":abhf:o:")) != -1) {
		switch(c) {
		case 'a':
			aflg++;
			break;
		case 'b':
			bflg++;
			break;
		case 'f':
			ifile = optarg;
			break;
		case 'o':
			ofile = optarg;
			break;
		case 'h':
			printusage();
			return 0;
			break;
		case ':': // -f, -o 옵션에 오퍼랜드 없는경우.
			fprintf(stderr, "-%c 옵션은 피연산자가 필요함\n", optopt);
			errflg++;
			break;
		case '?':
			if (isprint (optopt)) {
				fprintf(stderr, "알 수 없는 옵션: -%c\n", optopt);
			} else {
				fprintf (stderr, "알 수 없는 옵션 문자: \\x%x.\n", optopt);
			}
			errflg++;
			break;
		}
	}
	if (errflg) {
		printusage();
		return 2;
	}
	
	printf("\t-a: %d\n\t-b: %d\n\t-f: %s\n\t-o: %s\n", aflg, bflg);
	for (; optind < argc; optind++) {
		printf("\t인자: %s\n", argv[optind]);
	}
	return 0;
}
root@wl ~ # ./getopt -a -b -f 입력파일 -o 출력파일 인자1 인자2
        -a: 1
        -b: 1
        -f: 출력파일
        -o: 입력파일
        인자: 인자1
        인자: 인자2
root@wl ~ #

getopt_long

getopt_long(3C)는 위에서 소개한 가이드라인의 GNU 확장이다. 하지만 사실상 업계표준으로써 많이 사용되고 있으며, 솔라리스에 번들된 명령에서도 많이 사용한다.
getoptlong.c
(2,017 바이트)
/*
	getopt_long sample
	WindyHana's Solanara http://www.solanara.net/
	cc -o getoptlong getoptlong.c
*/
#include <stdio.h>
#include <unistd.h>
#include <getopt.h>

void printusage() {
	printf("\n");
	printf("getoptlong 예제\n");
	printf("\n");
	printf("사용법: getoptlong [옵션]... [인자]...\n");
	printf("옵션:\n");
	printf("  -a,\t--aflag\t\taflg 세팅\n");
	printf("  -b,\t--bflag\t\taflg 세팅\n");
	printf("  -i,\t--iflag 파일명\tifile 세팅\n");
	printf("  -o,\t--oflag 파일명\tofile 세팅\n");
	printf("  \t--long\t\tislong을 1로 세팅\n");
	printf("  \t--short\t\tislong을 0로 세팅\n");
	printf("인자:\n");
	printf("	출력할인자\n");
	printf("\n");
}

int main(int argc, char *argv[]) {
	int c;
	static int islong = 0;
	int aflg = 0, bflg = 0, errflg = 0;
	char *ifile = "NONE";
	char *ofile = "NONE";
	
	while (1) {
		static struct option long_options[] = {
			{"long",  no_argument, &islong, 1},
			{"short", no_argument, &islong, 0},
			{"aflag", optional_argument, NULL, 'a'},
			{"bflag", optional_argument, NULL, 'b'},
			{"ifile", required_argument, NULL, 'i'},
			{"ofile", required_argument, NULL, 'o'},
			{0, 0, 0, 0}
		};
		
		int option_index = 0;
		c = getopt_long(argc, argv, "abhi:o:",  long_options, &option_index);
		if (c == -1) break;

		switch (c) {
			case 0: // --long, --short 옵션을 주면 islong 변수에 지정된 값이 세팅되고, 0 으로 리턴된다.
				break;
			case 'a':
				aflg++;
				break;
			case 'b':
				bflg++;
				break;
			case 'i':
				ifile = optarg;
				break;
			case 'o':
				ofile = optarg;
				break;
			case 'h':
				printusage();
				return 0;
				break;
			default:
				// 각종 오류 메시지는 getopt_long 에서 출력해준다
				errflg++;
		}
	}

	if (errflg) {
		printusage();
		return 2;
	}
	
	printf("\t-islong: %d\n", islong);
	printf("\t-a: %d\n\t-b: %d\n\t-i: %s\n\t-o: %s\n", aflg, bflg, ifile, ofile);
	for (; optind < argc; optind++) {
		printf("\t인자: %s\n", argv[optind]);
	}
	return 0;
}
root@wl ~ # ./getoptlong --long --short --aflag -b -i 입력파일 -o 출력파일 인자1 인자2
        -islong: 0
        -a: 1
        -b: 1
        -i: 입력파일
        -o: 출력파일
        인자: 인자1
        인자: 인자2
root@wl ~ # 

getopt 명령

셸 스크립트에서도 getopt 를 사용하기 위한 getopt(1) 에 대해 소개한다. 사용방법은 getopt와 비슷하다.
getopt.sh
(316 바이트)
#!/bin/bash
GETOPT=`/usr/bin/getopt abo: $*`
if [ $? != 0 ]; then
	echo
	echo USEAGE: $0 [-ab][-o OARG]
	echo
	exit 2
fi
set -- "$GETOPT"

FLAG=""
OARG=""
for i in $*
do
	case $i in
		-a | -b)
			FLAG=$i; shift ;;
		-o)
			OARG=$2; shift 2 ;;
		--)
			shift; break ;;
	esac
done

echo "FLAG=$FLAG"
echo "OARG=$OARG"

gettext

gettext.c
(414 바이트)
/*
	gettext sample
	WindyHana's Solanara http://www.solanara.net/
	cc -o gettext gettext.c
*/
#include <locale.h>
#include <libintl.h>
#include <stdio.h>

#define LOCALEDIR "./locales"
#define PACKAGE "gettextdomain"

int main(int argc, char *argv[]) {
	setlocale(LC_ALL, "");
	bindtextdomain(PACKAGE, LOCALEDIR);
	textdomain(PACKAGE);
	// cannot support '_' on solaris
	printf("%s\n", gettext("Hello world!"));
}
root@wl ~ # cc -o gettext gettext.c
root@wl ~ # ./gettext
Hello world!
root@wl ~ # mkdir -p ./locales/$LANG/LC_MESSAGES
root@wl ~ # xgettext -d gettextdomain gettext.c
root@wl ~ # cp gettextdomain.po ko.po
root@wl ~ # vi ko.po
domain "gettextdomain"
msgid  "Hello world!"
msgstr "안녕하세요!"
root@wl ~ # msgfmt -o ./locales/$LANG/LC_MESSAGES/gettextdomain.mo ko.po
root@wl ~ # ./gettext
안녕하세요!
root@wl ~ #
RSS ATOM XHTML 5 CSS3