유틸리티 규약, getopt, gettext - WindyHana's Solanara
목차
개요
유닉스에는 유틸리티의 인자에 대한 가이드라인이 있다. 커맨드의 인자들은 모두 이 가이드라인에 맞춰 제작되기 때문에 이 가이드라인만 알아두면 유틸리티를 사용하는데 쉬워질 것이다.
참조
유틸리티 규약
강제성은 없으며, 프로그래머를 위한 지침일 뿐이지만, 이 규약을 알고 있다면, 규약을 지킨 유틸리티를 더 쉽게 사용할 수 있다. 이 규약이 만들어지기 이전부터 있던 솔라리스에 번들된 유틸리티는 이 규약을 지키지 않은 것도 있다.
유틸리티의 이름은 2자 이상 9자 이하이어야 한다.
유틸리티의 이름은 소문자를 포함하고 있어야 한다.
각각의 옵션명은 영문/숫자로 1자이어야 한다. -W는 벤더별 옵션으로 사용한다. 1)
모든 옵션은 대시(-)로 시작한다.
옵션인자가 없는 옵션은 -으로 그룹화 해서 사용할 수 있다. ([ls -a -l]과 [ls -al]이 같음을 의미한다)
각 옵션과 옵션인자는 공백문자로 구분되어야 한다.
옵션인자(option arguments, 옵션의 인자, [cc -o abc abc.c]에서, abc 가 -o 옵션의 옵션인자에 해당된다)는 옵션을 사용한다면 반드시 입력해야 한다.
옵션에 대해 두개 이상의 옵션인자가 필요하다면 각 옵션인자는 [,]나 공백문자로 옵션 안에서 구분할 수 있도록 해야 한다.
모든 옵션은 명령라인에서 피연산자(operand) 앞에 사용되어야 한다.
[--] 는 옵션의 끝을 의미한다. [--] 뒤에 나오는 -로 시작하는 오퍼랜드를 더이상 옵션으로 인식하지 않는다. ([ls -al -- -a]명령은 -a 라는 파일을 -al 형식으로 출력하라는 의미다)
옵션의 순서는 상관 없이 정하지만, 옵션인자의 입력은 프로그램의 특성상 순서가 중요할 수 있다. 먼저 입력한 옵션인자를 먼저 처리한다. ([make -f makefile1.mk -f makefile2.mk all]와 같은 명령에서 makefile1.mk를 먼저 처리해야 한다는 의미다)
오퍼랜드의 순서에 대한 의미는 프로그램의 특성을 따른다. ([cp a b]명령은 오퍼랜드의 순서가 중요하다)
파일을 의미하는 오퍼랜드 또는 옵션인자에서 - 는 stdin 또는 stdout 을 의미한다.
1) 같은 유틸리티를 다른 유닉스에 포팅할 때, 특정한 유닉스를 위한 전용 옵션은 -W를 쓰라는 의미다. 특별한 의미를 지닌 다른 옵션으로 -X가 있는데 비 표준 옵션을 의미한다.
이 규약을 지키기 위해, 프로그래머를 위해 만들어 놓은 함수가 getopt 이다.
유틸리티 규약 예
커맨드 [-a] [-b] [-c 옵션인자1] [-d|-e] [-f옵션인자2] -g 옵션인자3 [오퍼랜드...]
유틸리티의 이름은 [커맨드]이다.
-a, -b 를 옵션으로 줄 수 있다.
-c는 옵션이지만 사용하는 경우 옵션인자를 반드시 주어야 한다.
-d와 -e 중 한가지만 옵션으로 사용할 수 있다.
-f처럼 옵션 인자를 사용할 수도 있다.
-g 옵션인자는 옵션인자와 함께, 커맨드를 실행하기 위해 반드시 사용해야 하는 인자다.
오퍼랜드를 여러개 줄 수 있다.
위 문법은 아래와 같이 표현할 수도 있다. 실제로 [-a -b]대신 [-ab]로 넣어도 동일하게 작동한다.
커맨드 [-ab] [-c 옵션인자] [-d|-e] [-f옵션인자] -g 옵션인자3 [오퍼랜드...]
getopt
많은 프로그램들이 옵션을 처리하기 위해서 getopt(3C) 를 사용하고 있을 것이다. 솔라리스의 getopt 는 GNU getopt 의 형식을 많이 따르고 있다.
/*
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 확장이다. 하지만 사실상 업계표준으로써 많이 사용되고 있으며, 솔라리스 번들된 명령문들도 많이 사용한다.
/*
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와 비슷하다.
#!/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는 LC_MESSAGES 로케일에 의해 메시지를 얻어오는 함수다. 주로 다국어 지원을 위해 사용한다. getopt 와 함께 자주 사용되는 함수다. 솔라리스의 gettext 는 GNU gettext 와 호환된다. gettxt(3c) 와는 다른 함수다.
gettext(1) , gettext(3c) , xgettext(1) , msgfmt(1)
/*
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 ~ #