6、改变getopt()对错误命令行参数信息的输出行为
不正确的调用程序在所难免,这种错误要么是命令行选项无效,要么是缺少选项参数。正常情况下,getopt()会为这两种情况输出自己的出错信息,并且返回'?'。为了验证此事,可以修改一下上面的清单2中的代码。
- 清单3:#include <stdio.h>
- #include <unistd.h>
- int main (int argc, char **argv)
- {
- int oc; /*选项字符 */
- char *b_opt_arg; /*选项参数字串 */
- while((oc = getopt(argc, argv, "ngl:")) != -1)
- {
- switch(oc)
- {
- case 'n':
- printf("My name is Lyong./n");
- break;
- case 'g':
- printf("Her name is Xxiong./n");
- break;
- case 'l':
- b_opt_arg = optarg;
- printf("Our love is %s/n", optarg);
- break;
- case '?':
- printf("arguments error!/n");
- break;
- }
- }
- return 0;
- }
- 输入一个错误的命令行,结果如下:$ ./opt_parse_demo -l
- ./opt_parse_demo: option requires an argument -- l
- arguments error!
很多时候,我们不希望输出任何错误信息,或更希望输出自己定义的错误信息。可以采用以下两种方法来更改getopt()函数的出错信息输出行为:
在调用getopt()之前,将opterr设置为0,这样就可以在getopt()函数发现错误的时候强制它不输出任何消息。
如果optstring参数的第一个字符是冒号,那么getopt()函数就会保持沉默,并根据错误情况返回不同字符,如下:
“无效选项” —— getopt()返回'?',并且optopt包含了无效选项字符(这是正常的行为)。
“缺少选项参数” —— getopt()返回':',如果optstring的第一个字符不是冒号,那么getopt()返回'?',这会使得这种情况不能与无效选项的情况区分开。
- 清单4:#include <stdio.h>
- #include <unistd.h>
- int main (int argc, char **argv)
- {
- int oc; /*选项字符 */
- char ec; /*无效的选项字符*/
- char *b_opt_arg; /*选项参数字串 */
- while((oc = getopt(argc, argv, ":ngl:")) != -1)
- {
- switch(oc)
- {
- case 'n':
- printf("My name is Lyong./n");
- break;
- case 'g':
- printf("Her name is Xxiong./n");
- break;
- case 'l':
- b_opt_arg = optarg;
- printf("Our love is %s/n", optarg);
- break;
- case '?':
- ec = (char)optopt;
- printf("无效的选项字符 /' %c /'!/n", ec);
- break;
- case ':':
- printf("缺少选项参数!/n");
- break;
- }
- }
- return 0;
- }
- 测试结果:$ ./opt_parse_demo -a
- 无效的选项字符 ' a '!
- $ ./opt_parse_demo -l
- 缺少选项参数!
7、GNU提供的getopt()函数的特点
上 面所设计的getopt()函数是UNIX支持小组提供的,其执行时一碰到不以'-'开始的命令行参数就停止寻找选项。而GNU提供的getopt()函 数与之不同,它会扫描整个命令行来寻找选项。当调用GNU getopt()函数并处理命令行参数的时候,它重新排列argv中的元素,这样当重排结束时,所有选项都被移动到前面并且那些继续检查argv [optind]至argv[argc-1]中剩余参数的代码仍正常工作,但在任何情况下,碰到特殊参数'--'就结束对选项的扫描。
可以输入一个乱序的命令行,查看opt_parse_demo的输出:
- $ ./opt_parse_demo -l forever a b c d -g -n
- Our love is forever
- Her name is Xxiong.
- My name is Lyong.
GNU getopt()第二个特点是可以在optstring中使用特殊的首字符改变getopt()的默认行为:
optstring[0] = '+',这样就与UNIX支持小组提供的getopt()很相近了。
optstring[0] = '-',会在optarg中得到命令行中的每个参数。
以上两种情况下,':'可以作为第二个字符使用。
GNU getopt()第三个特点是optstring中的选项字符后面接两个冒号,就允许该选项有可选的选项参数。在选项参数不存在的情况下,GNU getopt()返回选项字符并将optarg设置为NULL。
8、GNU长选项命令行解析
20 世纪 90 年代,UNIX 应用程序开始支持长选项,即一对短横线、一个描述性选项名称,还可以包含一个使用等号连接到选项的参数。
GNU提供了getopt-long()和getopt-long-only()函数支持长选项的命令行解析,其中,后者的长选项字串是以一个短横线开始的,而非一对短横线。
getopt_long() 是同时支持长选项和短选项的 getopt() 版本。下面是它们的声明:
- #include <getopt.h>
- int getopt_long(
- int argc, char * const argv[],
- const char *optstring,
- const struct option *longopts,
- int *longindex
- );
- int getopt_long_only(
- int argc, char * const argv[],
- const char *optstring,
- const struct option *longopts,
- int *longindex
- );
getopt_long ()的前三个参数与上面的getopt()相同,第4个参数是指向option结构的数组,option结构被称为“长选项表”。longindex参数 如果没有设置为NULL,那么它就指向一个变量,这个变量会被赋值为寻找到的长选项在longopts中的索引值,这可以用于错误诊断。
option结构在getopt.h中的声明如下:
- struct option{
- const char *name;
- int has_arg;
- int *flag;
- int val;
- };
对结构中的各元素解释如下:
const char *name----这是选项名,前面没有短横线。譬如"help"、"verbose"之类。
int has_arg ----------描述了选项是否有选项参数。如果有,是哪种类型的参数,此时,它的值一定是下表中的一个。符号常量数值含义 .
no_argument 0-------选项没有参数
required_argument 1 --选项需要参数
optional_argument 2 -- 选项参数可选
int *flag --------------如果这个指针为NULL,那么getopt_long()返回该结构val字段中的数值。如果该指针不为NULL,getopt_long()会使得它所指 向的变量中填入val字段中的数值,并且getopt_long()返回0。如果flag不是NULL,但未发现长选项,那么它所指向的变量的数值不变。
int val -----------------这 个值是发现了长选项时的返回值,或者flag不是NULL时载入*flag中的值。典型情况下,若flag不是NULL,那么val是个真/假值,譬如1 或0;另一方面,如果flag是NULL,那么val通常是字符常量,若长选项与短选项一致,那么该字符常量应该与optstring中出现的这个选项的 参数相同。
每个长选项在长选项表中都有一个单独条目,该条目里需要填入正确的数值。数组中最后的元素的值应该全是0。数组不需要排序,getopt_long()会进行线性搜索。但是,根据长名字来排序会使程序员读起来更容易。
大部分时候,程序员会根据getopt_long()发现的选项,在选项处理过程中要设置一些标记变量,譬如在使用getopt()时,经常做出如下的程序格式:int do_name, do_gf_name, do_love; /*标记变量*/
- char *b_opt_arg;
- while((c = getopt(argc, argv, ":ngl:")) != -1)
- {
- switch (c){
- case 'n':
- do_name = 1;
- case 'g':
- do_gf_name = 1;
- break;
- break;
- case 'l':
- b_opt_arg = optarg;
- ……
- }
- }
当flag 不为NULL时,getopt_long*()会为你设置标记变量。也就是说上面的代码中,关于选项'n'、'l'的处理,只是设置一些标记,如果 flag不为NULL,时,getopt_long()可以自动为各选项所对应的标记变量设置标记,这样就能够将上面的switch语句中的两种种情况减 少到了一种。下面给出一个长选项表以及相应处理代码的例子。
- #include <stdio.h>
- #include <getopt.h>
- int do_name, do_gf_name;
- char *l_opt_arg;
- struct option longopts[] = {
- { "name", no_argument, &do_name, 1 },
- { "gf_name", no_argument, &do_gf_name, 1 },
- { "love", required_argument, NULL, 'l' },
- { 0, 0, 0, 0},
- };
- int main(int argc, char *argv[])
- {
- int c;
- while((c = getopt_long(argc, argv, ":l:", longopts, NULL)) != -1){
- switch (c){
- case 'l':
- l_opt_arg = optarg;
- printf("Our love is %s!/n", l_opt_arg);
- break;
- case 0:
- printf("getopt_long()设置变量 : do_name = %d/n", do_name);
- printf("getopt_long()设置变量 : do_gf_name = %d/n", do_gf_name);
- break;
- }
- }
- return 0;
- }
- 下面测试一下:$ ./long_opt_demo --name
- getopt_long()设置变量 : do_name = 1
- getopt_long()设置变量 : do_gf_name = 0
- $ ./long_opt_demo --gf_name
- getopt_long()设置变量 : do_name = 0
- getopt_long()设置变量 : do_gf_name = 1
- $ ./long_opt_demo --love forever
- Our love is forever!
- $ ./long_opt_demo -l forever
- Our love is forever!
测试过后,应该有所感触了。关于flag和val的讨论到此为止。下面总结一下get_long()的各种返回值的含义:
0 getopt_long()设置一个标志,它的值与option结构中的val字段的值一样
1 每碰到一个命令行参数,optarg都会记录它
'?' 无效选项
':' 缺少选项参数
'x' 选项字符'x'
-1 选项解析结束
从实用的角度来说,我们更期望每个长选项都对应一个短选项,这种情况下,在option结构中,只要将flag设置为NULL,并将val设置为长选项所对应的短选项字符即可。
- :#include <stdio.h>
- #include <getopt.h>
- int do_name, do_gf_name;
- char *l_opt_arg;
- struct option longopts[] = {
- { "name", no_argument, NULL, 'n' },
- { "gf_name", no_argument, NULL, 'g' },
- { "love", required_argument, NULL, 'l' },
- { 0, 0, 0, 0},
- };
- int main(int argc, char *argv[])
- {
- int c;
- while((c = getopt_long(argc, argv, ":l:", longopts, NULL)) != -1){
- switch (c){
- case 'n':
- printf("My name is LYR./n");
- break;
- case 'g':
- printf("Her name is BX./n");
- break;
- case 'l':
- l_opt_arg = optarg;
- printf("Our love is %s!/n", l_opt_arg);
- break;
- }
- }
- return 0;
- }
- 测试结果如下:$ ./long_opt_demo --name --gf_name --love forever
- My name is LYR.
- Her name is BX.
- Our love is forever!
- $ ./long_opt_demo -ng -l forever
- My name is LYR.
- Her name is BX.
- Our love is forever!
这些信息可以让你很清晰地了解到命令行参数的解析,有时候在编程的过程中,能给你带来很大的方便,当然,只能说在你需要它的时候,在下面一节我将会通过与g_spawn_command_line_async的结合写出一个实例.
~~END~~