http://blog.csdn.net/luckheadline
一. 命令行规范
关于实现命令行参数, C 语言有着由来已久的传统规范。
1. 可选命令行参数前有一个“ - ”, DOS 或 WINDOWS 系统也可以用“ / ”。
2. 多个可选命令行参数之间没有顺序要求,每个参数都可以放置在参数列表中的任何位置。
3. 可选命令行参数本身可能会带有参数。
4. 可能有些命令行程序并不遵从以上的规范,虽然这样做并不推荐。
二. 如何实现命令行参数处理(以 C 语言为例)
命令行参数总是表现为一个字符串数组,这个字符串数组被称为 ”argv”( 代表 arguments values), 还有一个整数 argc( 代表 arguments count) 用来表示命令行参数列表中参数的个数。因此,我们的 main 函数的格式如下:
int main(int argc, char* argv[]), 务必要记住 argv[0] 是程序的名字,在 usage 声明中你可能会用到它。 main 函数中的第一任务是实现可选参数,操作 argv 的标准方式如下所示:
- int i;
- int quiet=0; //Value for the “-q” optional argument.
- for (i = 1; i < argc; i ++) //skip argv[0].
- {
- /*
- *Use the ‘strcmp’ function to compare the argv values to a string
- *of your choice(here, it’s a *optinal argument “-q”). When
- *strcmp returns 0, it means that the two strings are identical.
- */
- if (strcmp(argv[i], “-q”) == 0) //Process optional arguments
- {
- quiet = 1; //This is used as a Boolean value.
- }
- else
- {
- //Process non-optional arguments here.
- }
- }
请注意,正如这段代码所示,可选参数“
-q ”可以被放置于参数列表的任何位置而且程序都可以正常 work 。 如果可选参数本身还有参数的话,那么代码将更加的 trickier :
- int i;
- int opt = 0;
- int optarg1 = 0;
- int optarg2 = 0;
- for (i = 1; i < argc; i ++)
- {
- if (strcmp(argv[i], “-opt”) == 0)
- {
- opt = 1; //This is used as a boolean value.
- /*
- *The last argument is argv[argc -1]. Make sure there
- *are enough arguments.
- */
- if (i + 2 <= argc - 1) //There are enough arguments in argv.
- {
- /*
- *Increment ‘i’ twice so that you don’t check
- *these arguments the next time through the loop.
- */
- i ++;
- optarg1 = atoi(argv[i]); //Convert string to int
- i ++;
- optarg2 = atoi(argv[i]);
- }
- else
- {
- //Print usage statement and exit.
- }
- }
- else
- {
- //Process non-optional arguments here.
- }
- }
在很多情况下,命令行处理可能会非常麻烦,不过上面的代码已经可以应付相当多的情况了。
BTW, 在 Linux 下,习惯上会有两种形式的命令行参数,一种是短形式,通常由 ”-” 加一个字母组成,这种形式的好处是便捷;另一种是长形式,通常由 ”--” 加一个单词组成,这种形式的好处是形象,好记,直观。
三. Usage 声明
有时候,用户可能会输入不合法的命令行参数,你的程序需要处理这种情况以避免程序 crash 。这种情况的正确处理方式是:
1. 显示 usage 声明
2. 退出程序
例如,假设你的程序除了程序名还期望有 3 个参数,并且要接受另外一个可选参数:
usage 声明的以下几点意见:
1. usage 信息 : 总是使用“ usage ”开头,接着是程序名和参数名。参数名应该尽可能的指明参数的意义,比如上面的 filename 。参数名不应该包含空格!可选参数使用 [] ,比如上面程序中的 ”-w” 。非可选参数不要使用 [] !总是打印 stderr ,而非 stdout ,以指明程序没有正确 invoke 。
2. 程序名:总是使用 argv[0] 以指明程序名而非显式的指出。因为这样,当你重新命名程序之后,不用重写代码。
3. 退出程序:使用 exit 函数( exit 函数被定义在头文件 <stdlib.h> )。 exit 函数的任何非零参数都标志着程序的非正常结束( exit ( 0 )标识程序的正常执行,但你很少会像这样用)。你也可以使用宏 EXIT_FAILURE 和 EXIT_SUCCESS 以取代 1 和 0 作为 exit 函数的参数。
如果不得不多次写 usage 声明,你可以将它分离出来作为一个独立的函数,并且将程序名( argv[0] )作为参数传给它。那样它就可以被 main 函数在任何需要的时候调用了。
四. 常见错误
1. 当程序接收到不正确参数时,应该总是打印一个 usage 消息给 stderr 。如果处理失败将会自动 redo 。
2. 不要假设可选参数会固定放在参数列表的某个位置上。
3. 如果不方便的话,就不要尝试一遍处理所有的命令行参数。
4. 不要修改 argv 数组。
五. getOpt() 的使用
getOpt() 是一个专门设计来减轻命令行处理负担的库函数。它位于 unistd.h 系统头文件中,其原型如下:
int getopt(int argc, char *const argv[], const char *optstring);
函数给定了命令参数的数量 (argc) ,指向这些参数的数组 (argv) 和选项字符串 (optstring) 后, getopt() 将返回第一个选项,并设置一些全局变量。使用相同的参数再次调用该函数时,它将返回下一个选项,并设置相应的全局变量。如果不再有识别到的选项,将返回 -1 ,此任务就完成了。
getopt() 所设置的全局变量包括:
optarg :指向当前选项参数(如果有)的指针。
optind :再次调用 getopt() 时的下一个 argv 指针的索引。
optopt :最后一个已知选项。
可以重复调用 getopt() ,直到其返回 -1 为止;任何剩下的命令行参数通常视为文件名或称程序相应的其他内容。见以下代码示例:
Unix 程序还支持长选项,可以通过使用 getopt_long() 向程序添加长选项支持,因为 getopt_long() 是同时支持长选项和短选项的 getopt() 版本。 getopt_long() 函数还接受其他参数,其中一个是指向 struct option 对象数组的指针。结构如下:
成员是指向长选项名称(带两个短横线)的指针。 has_arg 成员设置为 no_argument, optional_argument, 或 required_argument (均在 getopt.h 中定义)之一,以指示选项是否具有参数。如果 flag 成员未设置为 NULL ,在处理期间遇到此选项时,会使用 val 成员的值填充它所指向的 int 值。如果 flag 成员为 NULL ,在 getopt_long() 遇到此选项时,将返回 val 中的值;通过将 val 设置为选项的 short 参数,可以在不添加任何其他代码的情况下使用 getopt_long()—— 处理 while loop 和 switch 的现有 getopt() 将自动处理此选项。代码示例可以参见 reference 5 。
Reference:
http://www.cs.caltech.edu/courses/cs11/material/c/mike/misc/cmdline_args.html
http://www.ibm.com/developerworks/cn/linux/shell/clutil/index.html
http://linux.chinaitlab.com/command/4198.html
http://www.gnu.org/software/libtool/manual/libc/Getopt.html
http://www.ibm.com/developerworks/cn/aix/library/au-unix-getopt.html