命令行参数的解析
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()
中转换说明的作用.
optstring
is 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
W
followed by a semicolon, then−W foo
is treated as the long option−−foo
. (The−W
option is reserved byPOSIX.2
for 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_CORRECT
is 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-
inoptstring
is 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 setval
to the equivalent short option character.)- Otherwise,
getopt_long()
returns0
, andflag
points to a variable which is set toval
if 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
longindex
is 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 ↩