Linux下开发C程序,甚至是GUI程序,都可能需要处理复杂的命令行参数。健全、可靠的复杂命令行参数处理机制,可使程序方便使用,也更显专业。Linux下几乎所有的命令都提供了参数处理机制,包括短选项和长选项。
POSIX标准中对程序名、参数作了如下相关约定:
* 程序名不宜少于2个字符且不多于9个字符;
* 程序名应只包含小写字母和阿拉伯数字;
* 选项名应该是单字符活单数字,且以短横‘-‘为前綴;
* 多个不需要选项参数的选项,可以合并。(譬如:foo -a -b -c ---->foo -abc)
* 选项与其参数之间用空白符隔开;
* 选项参数不可选。
* 若选项参数有多值,要将其并为一个字串传进来。譬如:myprog -u "arnold,joe,jane"。这种情况下,需要自己解决这些参数的分离问题。
* 选项应该在操作数出现之前出现。
* 特殊参数‘--'指明所有参数都结束了,其后任何参数都认为是操作数。
* 选项如何排列没有什么关系,但对互相排斥的选项,如果一个选项的操作结果覆盖其他选项的操作结果时,最后一个选项起作用;如果选项重复,则顺序处理。
* 允许操作数的顺序影响程序行为,但需要作文档说明。
* 读写指定文件的程序应该将单个参数'-'作为有意义的标准输入或输出来对待。
GNU鼓励程序员使用--help、--verbose等形式的长选项。这些选项不仅不与POSIX约定冲突,而且容易记忆,另外也提供了在所有GNU工具之间保持一致性的机会。
GNU长选项有自己的约定:
* 对于已经遵循POSIX约定的GNU程序,每个短选项都有一个对应的长选项。
* 额外针对GNU的长选项不需要对应的短选项,仅仅推荐要有。
* 长选项可以缩写成保持惟一性的最短的字串。
* 选项参数与长选项之间或通过空白字符活通过一个'='来分隔。
* 选项参数是可选的(只对短选项有效)。
* 长选项允许以一个短横线为前缀。
C程序通过argc和argv参数访问它的命令行参数,通过main()函数调用和处理:int main(int argc, char *argv[])。一般情况下,我们事先约定好参数的顺序位置,然后在main函数中进行简单处理。这种方式实现比较简单,然后用户使用起来很不方便,Linux下的各种工具的命令行参数可以是不分先后次序的。幸运的是,Linux为C程序员提供了相关的命令行参数解析函数:getopt()和getopt_long(),分别用于处理短选项和长选项,当然后者可以同时处理短、长选项。函数原型如下:
#include <unistd.h>
int getopt(int argc, char * const argv[], const char *optstring);
extern char *optarg;
extern int optind, opterr, optopt;
int getopt_long(int argc, char * const argv[], const char *optstring, const struct option *longopts, int *longindex);
getopt() 函数是一个标准库调用,可允许使用直接的 while/switch 语句方便地逐个处理命令行参数和检测选项(带或不带附加的参数)。getopt_long() 允许在几乎不进行额外工作的情况下处理更具描述性的长选项,非常受开发人员的欢迎。下面用笔者开发的一个Daemon程序wsiod中的参数处理来说明具体的处理过程。wsiod参数帮助信息如下:
[liuag] /home/liuag/workspace/WSIO/WSIO-1.3/server > ./wsiod --help
Usage: wsiod [OPTION]
WSIO server based on web service.
Mandatory arguments to long options are mandatory for short options too.
-h, --host hostname in soap_bind, default is host which the service runs
-p, --port port which the sercer runs on, default is 8080
-b, --backlog request backlog, default is 100
-t, --type server type, default is COMMON
-k, --keepalive attempt to keep socket connections alive
-c, --chunk use HTTP chunking
-d, --dime use DIME encoding
-D, --debug print debug info
--help print this help
Server type:
COMMON the simplest server
STANDALONE stand-alone server, which can run on port 80
MULTITHREAD multi-thread stand-alone server
POOL using a pool of servers
QUEUE using a queue of requests for server
GSI prethreaded server with GSI enabled
Report bugs to <Aigui.LIU@ihep.ac.cn>.
wsiod参数处理实现C程序段如下:
#include <unistd.h>
#include <getopt.h>
enum SERVERTYPE{COMMON, STANDALONE, MULTITHREAD, POOL, QUEUE, GSI};
int keepalive = 0, dime = 0, chunk = 0;
/* WSIO server main function */
int main(int argc, char **argv)
{
int c;
char host[128] = "localhost";
char log_buf[LOGBUFSZ];
int port = 8080, backlog = 100;
enum SERVERTYPE servertype = COMMON;
int helpflg = 0,
errflg = 0,
debug = 0;
struct option longopts[] =
{
{"host", 1, 0, 'h'},
{"port", 1, 0, 'p'},
{"backlog", 1, 0, 'b'},
{"type", 1, 0, 't'},
{"keepalive", 0, 0, 'k'},
{"chunk", 0, 0, 'c'},
{"dime", 0, 0, 'd'},
{"debug", 0, 0, 'D'},
{"help", 0, &helpflg, 1},
{0, 0, 0, 0}
};
while ((c = getopt_long (argc, argv, "h:p:b:t:kcdD", longopts, NULL)) != EOF)
{
switch(c)
{
case 'h':
sprintf(host, "%s", optarg);
break;
case 'p':
port = atoi(optarg);
break;
case 'b':
backlog = atoi(optarg);
break;
case 't':
switch(*optarg)
{
case 'C':
servertype = COMMON;
break;
case 'S':
servertype = STANDALONE;
break;
case 'M':
servertype = MULTITHREAD;
break;
case 'P':
servertype = POOL;
break;
case 'Q':
servertype = QUEUE;
break;
case 'G':
servertype = GSI;
default:
break;
}
break;
case 'k':
keepalive = 1;
break;
case 'c':
chunk = 1;
break;
case 'd':
dime = 1;
break;
case 'D':
debug = 1;
break;
case '?':
errflg++;
break;
default:
break;
}
}
if(helpflg || errflg)
{
fprintf(stderr,"Usage: wsiod [OPTION]/n/n%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s",
"WSIO server based on web service./n",
"Mandatory arguments to long options are mandatory for short options too./n",
"/t-h, --host hostname in soap_bind, default is host which the service runs/n ",
"/t-p, --port port which the sercer runs on, default is 8080/n",
"/t-b, --backlog request backlog, default is 100/n",
"/t-t, --type server type, default is COMMON/n",
"/t-k, --keepalive attempt to keep socket connections alive/n",
"/t-c, --chunk use HTTP chunking/n",
"/t-d, --dime use DIME encoding/n",
"/t-D, --debug print debug info/n",
"/t --help print this help/n/n",
"Server type:/n",
"/tCOMMON the simplest server/n",
"/tSTANDALONE stand-alone server, which can run on port 80/n"
"/tMULTITHREAD multi-thread stand-alone server/n",
"/tPOOL using a pool of servers/n",
"/tQUEUE using a queue of requests for server/n",
"/tGSI prethreaded server with GSI enabled/n/n",
"Report bugs to <Aigui.LIU@ihep.ac.cn>./n"
);
exit(0);
}
/* 省略部分 */
return 0;
}