命令行参数的解析
info
LICENSE
本文使用 GNU通用公共许可证 v3(GNU General Public License, version 3) 发布.
命令行参数的解析
真巧,在笔者近日的程序设计实践中又涉及到了命令行参数,笔者便再谈谈他.因单篇博客不宜过长,该内容将拆分在一系列博文中,该系列博文中,笔者将只讨论 getopt() 、 getopt_long() 、 getopt_long_only() 的使用,不涉及其他方案.
getopt() 的基本信息
1 |
|
上面有 getopt() 函数的函数原型和相关全局变量,注意使用 getopt() 函数需要包含 unistd.h.
getopt() 被用来处理遵循 Single UNIX Specification 的命令行参数
Single UNIX Specification 要求[1]:
限制每个命令行选项为一个单一的阿拉伯字符
所有选项必须以
-作为开头字符
举例来说就是getopt()可用于处理 command -t 123 -p 456.txt -uroot 这类命令,而不能用于 command --times 123 --path 456.txt --userroot .
getopt()的参数
argv、argc
这两个参数即为待解析的命令行参数的计数和指向字符串存储位置的指针数组.这两个参数的实参通常作为int main(int argc,char *argv[])的参数传入程序.对该处有疑问的读者可参考笔者的博文命令行参数的误解.
optstring
optstring是一个字符串,用来说明预期的命令行参数的格式.它的作用可能有点类似 scanf() 中转换说明的作用.
optstringis a string containing the legitimate option characters. If such a character is followed by a colon, the option requires an argument, sogetopt()places a pointer to the following text in the same argv-element, or the text of the following argv-element, inoptarg. Two colons mean an option takes an optional arg
- if there is text in the current argv-element (i.e., in the same word as the option name itself, for example,
−oarg), then it is returned in optarg, otherwise optarg is set to zero. This is a GNU extension.- If optstring contains
Wfollowed by a semicolon, then−W foois treated as the long option−−foo. (The−Woption is reserved byPOSIX.2for implementation extensions.) This behavior is a GNU extension, not available with libraries beforeglibc 2.
optstring 是包含合法选项字符的字符串.如果此类字符后接 : ,则该选项需要一个参数,因此 getopt() 将指针指向位于同一 argv 元素中的后续文本,或位于 argarg 中的后续 argv 元素的文本. :: 表示一个选项带有一个可选的参数
- 如果当前argv元素中有文本( 例如与选项名称本身相同的词,例如
-oarg),则将其以optarg返回,否则optarg设置为0.这是一个 GNU 扩展 . - 如果
optstring包含W后跟一个分号,则将-W foo视为长选项--foo.(-W选项由POSIX.2保留用于实现扩展.)此行为是 GNU 扩展 ,不适用于glibc 2之前的库.
getopt()的返回值
If an option was successfully found, then
getopt()returns the option character.If all command-line options have been parsed, then
getopt()returns−1.If
getopt()encounters an option character that was not in optstring, then?is returned.If
getopt()encounters an option with a missing argument, then the return value depends on the first character in optstring:
- if it is
:, then:is returned- otherwise
?is returned.[3]
用笔者糟糕的英语勉强翻译一下:
warning
受限于笔者低劣的英文水平,笔者的翻译可能存在众多谬误,更无法做到 信、达、雅 的要求.笔者提供的翻译内容仅供参考.建议读者自行翻译或直接阅读英文原文.笔者不为本文中翻译内容的准确性负责.
如果一个选项被成功的找到,
getopt()返回这个选项的字母如果完成了所有的选项的解析,
getopt()将返回-1- 如果发现不在
optstring中的选项,则返回? - 如果发现丢失参数的选项,返回值取决于
optstring[0]:- 如果
optstring[0]是:,则返回: - 否则返回
?,即return optstring[0] == ':' ? ':' : '?';
- 如果
getopt() 的扫描模式
- If the first character of optstring is
+or the environment variablePOSIXLY_CORRECTis set, then option processing stops as soon as a nonoption argument is encountered.- If the first character of optstring is
−, then each nonoption argv-element is handled as if it were the argument of an option with character code1. (This is used by programs that were written to expect options and other argv-elements in any order and that care about the ordering of the two.)
- The special argument
−−forces an end of option-scanning regardless of the scanning mode.[3]
还是由笔者来翻译一下
如果
optstring[0]是+或者 环境变量POSIXLY_CORRECT被设置,则getopt()将会在遇到非optsting中的选项时停止.如果
optstring[0]是-,则任何一个非选项的argv中的元素将被按照ASCII 编码为1的字符处理.(这常被用于期待 选项 和argv 的元素按某种顺序排列并关注两者的顺序的程序 )特殊的参数
--将强制结束选项扫描,无论扫描模式是什么.
请看示例:
1 | /* getopt1.c */ |
请读者们编译后以参数 -a -- -c-- -- -g 运行程序.
笔者得到的输出内容
1 | ch 函数的返回值 a |
请读者注意:-- 作为 选项的参数 被读取时 getopt() 正常的的返回 选项字符 ,但当 -- 不作为 选项的参数 被读取时,getopt() 返回值为 -1 ,循环中止.
danger
The use of
+and-inoptstringis a GNU extension.[3]
在 optstring中使用+ 和 - 是一个 GNU 扩展.
这意味着使用+ 和 -的程序在不兼容 GNU 扩展 的编译器上 可能 无法正常的编译或运行.
如果在编译中使用了的-std=c99 、-std=c11 等指定 C语言标准 的编译选项需对应替换成 -std=gnu99 、-std=gnu11 .
getopt() 的错误处理
While processing the option list,
getopt()can detect two kinds of errors:
an option character that was not specified in optstring
a missing option argument (i.e., an option at the end of the command line without an expected argument).
Such errors are handled and reported as follows:
By default,
getopt()prints an error message on standard error, places the erroneous option character in optopt, and returns?as the function result.If the caller has set the global variable opterr to zero, then
getopt()does not print an error message. The caller can determine that there was an error by testing whether the function return value is?. (By default, opterr has a nonzero value.)If the first character (following any optional
+or−described above) of optstring is a colon (:), then getopt() likewise does not print an error message. In addition, it returns:instead of?to indicate a missing option argument. This allows the caller to distinguish the two different types of errors.[3]
笔者翻译成中文便是
在处理选项列表时, getopt() 可以检测两种错误:
未在
optstring中指定的选项字符选项缺少参数(例如,命令行末尾没有预期参数的选项)
这些错误的处理和报告如下:
- 默认情况下,
getopt()会在标准错误上显示一条错误消息,将错误的选项字符放入optopt,然后返回?作为函数结果. - 如果调用者已将全局变量
opterr设置为零,则getopt()不会输出错误消息. 调用方可以通过测试函数返回值是否为?来确定是否存在错误.(默认情况下,opterr具有非零值.) - 如果
optstring的第一个字符(上述任意可选的+或-之后)是冒号(:),则getopt()同样不会输出错误消息. 另外,它返回:而不是?表示缺少选项参数. 这使调用者可以区分两种不同类型的错误.
与 getopt() 相关的全局变量
其后,来讨论与 getopt() 相关的 4 个 全局变量 .
optarg如果一个选项需要参数,在处理该选项时,getopt会设置optarg指向该选项的参数字符串.
opterr如果一个选项发生了错误,getopt会默认打印一条出错消息.应用程序可以通过设置opterr参数为0来禁止这个行为.
optind用来存放下一个要处理的字符串在argv数组里的下标.它从1开始,每处理一个参数,getopt都会对其递增1.
optopt如果处理选项时发生了错误,getopt会设置optopt指向导致出错的选项字符串.[1]
请看示例:
1 | /* getopt1.c */ |
这段代码将演示getopt()的使用方法与 getopt()调用中相关的变量的变化.
请读者一定要使用-a 234 -b -c456 -d 789 -e -f -h -g作为参数运行该程序,查看输出逐个分析原因.
笔者得到的输出内容
1 | ch 函数的返回值 a |
值得特别关注的是:
- 第
2段,-c456被解释为选项b的参数,而不是选项c和其参数456. - 第
4段,定义为含有可选参数的选项d的参数为null,而不是789,因为可选参数的选项的参数和选项间不能加空格,要使789为选项d的参数,则该写为-d789.
请读者再次以参数 -ab -c123 -de -fx 执行该程序.
笔者得到的输出内容
1 | ch 函数的返回值 a |
值得特别关注的是:
- 第
1段,b被解释为选项a的参数,而不是选项a和选项b.请将第1段 和 第4与 第5段比较,-fx被解释为了选项f和选项x. - 第
4段,定义为可选参数的选项d的参数为null,而不是789,因为含「可选参数的选项的参数」和选项间不能加空格,要使789为选项d的参数,则该写为-d789.长选项
长选项以两个「-」开头,长选项的参数写法可以为以下两种格式:「--arg=param」或「--arg param」.基本信息
1 |
|
getopt_long()
argc、argv不必解释含义.optstring:当程序只接受长选项时,optstring应该为""(空字符串),而不是NULL.longopts:是一个struct option的数组.
name
is the name of the long option.has_arg
is:no_argument(or 0) if the option does not take an argument;required_argument(or 1) if the option requires an argument; oroptional_argument(or 2) if the option takes an optional argument.flag
specifies how results are returned for a long option.
- If flag is
NULL, thengetopt_long()returnsval. (For example, the calling program may setvalto the equivalent short option character.)- Otherwise,
getopt_long()returns0, andflagpoints to a variable which is set tovalif the option is found, but left unchanged if the option is not found.val
is the value to return, or to load into the variable pointed to byflag.>The last element of the array has to be filled with zeros.[1]
也就是说:
name
选项名.has_arg
需要为no_argument(无参数)、required_argument(需要参数)、optional_argument(可选参数)这三个宏中的一个.flag、val
当解析到该长选项时:- 如果
flag为NULL,getopt_only()返回val.(例如,调用者设置val为对应的短选项字符) - 如果
flag不为NULL,getopt_only()返回0,并且flag指向的变量将被设置为val.当未解析的该选项时,flag指向的值不变.longopts数组的最后一个元素需要全部字段为0.
- 如果
If
longindexis notNULL, it points to a variable which is set to the index of the long option relative tolongopts.[1]
如果longinedx不是NULL,它指向的值将被设置为解析到的长选项在longopt中的索引.
getopt_long_only()
getopt_long_only() 与 getopt_long() 是相似的.但 - 开头的选项也被认为是选项,当选项以 - 开头时,getopt_long_only() 将认为他是一个长选项.当选项以 - 开头且不匹配长选项但却能匹配短选项时,getopt_long_only() 将这个选项解析为短选项.
整理整理思路.
对于一个以 - 开头的选项:
getopt_long()认为他是一个短选项getopt_long_only()认为他是一个长选项
换而言之,-ab在getopt_long()眼中解释为:「选项a和选项b」或者「选项a和选项a的参数b」;但是getopt_long_only()将他首先解释为「长选项ab」,除非longopts的数组中不包含一个name为ab的长选项.
示例
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
int
main(int argc, char **argv)
{
int c;
int digit_optind = 0;
while (1) {
int this_option_optind = optind ? optind : 1;
int option_index = 0;
static struct option long_options[] = {
{"add", required_argument, 0, 0 },
{"append", no_argument, 0, 0 },
{"delete", required_argument, 0, 0 },
{"verbose", no_argument, 0, 0 },
{"create", required_argument, 0, 'c'},
{"file", required_argument, 0, 0 },
{0, 0, 0, 0 }
};
c = getopt_long(argc, argv, "abc:d:012",
long_options, &option_index);
if (c == -1)
break;
switch (c) {
case 0:
printf("option %s", long_options[option_index].name);
if (optarg)
printf(" with arg %s", optarg);
printf("\n");
break;
case '0':
case '1':
case '2':
if (digit_optind != 0 && digit_optind != this_option_optind)
printf("digits occur in two different argv-elements.\n");
digit_optind = this_option_optind;
printf("option %c\n", c);
break;
case 'a':
printf("option a\n");
break;
case 'b':
printf("option b\n");
break;
case 'c':
printf("option c with value '%s'\n", optarg);
break;
case 'd':
printf("option d with value '%s'\n", optarg);
break;
case '?':
break;
default:
printf("?? getopt returned character code 0%o ??\n", c);
}
}
if (optind < argc) {
printf("non-option ARGV-elements: ");
while (optind < argc)
printf("%s ", argv[optind++]);
printf("\n");
}
exit(EXIT_SUCCESS);
}[3]
编译并以 ./getopt_long -add --append -d34 -1 --verbose 运行,程序的输出为:
1 | option a |
上面的代码中,如果把第
21 的代码中的 getopt_long 改成 get_long_only 再次编译并以相同的参数运行就会得到如下的输出:1 | option add with arg --append |
区别很明显.
getopt_long() 将 -add 解释为了 选项a、选项 d、选项 d 的参数 d.
getopt_long_only() 将 -add 解释为了 选项 add.
参考书籍
1. W.RichardStevens.Stephen.UNIX环境高级编程[M].第3版.戚正伟,译.北京:人民邮电出版社 ↩
2. C++ 命令行参数解析.[G/OL].CSDN.https://blog.csdn.net/qq_34719188/article/details/83788199 ↩
3. Linux Programmer’s Manual.[G/OL].https://man7.org/linux/man-pages/man3/getopt.3.html ↩