0%

getopt相关

c解析命令行用的.

1. 定义

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
#include <unistd.h>

int getopt(int argc, char * const argv[],
const char *optstring);

extern char *optarg;
extern int optind, opterr, optopt;

#include <getopt.h>

struct option {
const char *name;
int has_arg;
int *flag;
int val;
};
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);


#include <stdlib.h>
int getsubopt(char **optionp, char * const *tokens, char **valuep);

2. 短选项以'-'开头,长选项以"--"开头。

3. getopt()的使用:

  1. 只接受短选项;
  2. argcargv 都是main()传进去的参数。
  3. optstring 是要解析哪些命令行选项。短选项都是一个字符(一般是字母),字符后不带':',说明该选项不要参数;带1个':',说明强制要求参数;带2个"::",表示可选参数,如果携带参数,需要紧跟着选项,比如这样-oarg,不能是这样-o arg(强制参数的可以是这样);GNU有个扩展,-W foo相当于长选项--foo,所以设置选项字符的时候别用W
  4. optind 指向下一个要解析的选项,初始化设为1,用户可以重新设置,让getopt()重新开始解析;
  5. 解析命令行的时候,需要多次调用getopt();
  6. 解析到一个选项,getopt()返回该选项字符,并将 optind 置为下一个要解析的选项;如果该选项带强制参数,*optarg指向该强制参数;如果该选项有可选参数,且命令行中有参数,optarg指向该参数,如果命令行没参数,optarg*指向NULL;
  7. 全部解析完参数,getopt()返回-1, optind 会指向第一个不是选项的参数;
  8. getopt()会在解析参数时对参数列表重新排序,让不是选项的参数排到最后。还有另外两种工作模式,1)选项字符串以+开头,或者设置了POSIXLY_CORRECT环境变量,会让getopt()在遇到不是选项的参数就退出;2)选项字符串以-开头,会将所有不是选项的参数都当成是选项字符1的参数,这样可以保证所有的参数都是按顺序解析;
  9. 特殊的选项--会强制让getopt()中止解析,不管哪种工作模式都适用;
  10. 遇到不认识的选项,getopt()会打印出错信息,并将该选项字符放到opterr中,然后返回'?'; 用户在调用getopt()之前将opterr设为0,可以屏蔽该错误信息;
  11. 遇到不认识的选项,或者选项缺少参数时,getopt()返回'?'; 如果选项字符串第一个字符是':'(跟在'+', '-'之后),选项缺少参数时,会返回':'而不是'?'

4. getopt_long()的使用:

  1. 可以额外接受长选项;
  2. 短选项还在optstring定义,长选项使用longopts定义;
  3. 多了两个参数;
  4. 其中struct option的各个字段为: 1) name该长选项的名字,就是命令行输入的时候的--name,2) has_arg有3个值:no_argument(0)表示没参数,required_argument(1)表示强制要求参数,optional_argument(2)表示可选参数; 3) flag是配合val使用的,下面说;
  5. 如果flag为NULL, getopt_long()返回val;如果flag不为NULL,getopt_long()返回0,然后flag会指向val的值。就是长选项的参数解析可以放到最外层的switch进行,也可以在case 0:中,使用switch(*flag)去解析。
  6. struct option *longopts是个数组,最后一个元素必须填充0,
  7. longindex指向当前解析的第几个longopts选项,可以使用longopts[*longindex]得到相应的数组;

5. getsubopt()的使用:

  1. 解析逗号分割的次级选项,比如这样的"-o rw,name=xyz";
  2. optionp是待解析的选项列表, tokens是可以解析哪些选项,就是rw, name这种,valuep就是解析出来的值,比如xyz这种;
  3. getsubopt()返回的就是tokens的索引,tokens是类似这样定义:
    1
    2
    3
    4
    5
    6
    char *const token[] = {
    [RO_OPT] = "ro",
    [RW_OPT] = "rw",
    [NAME_OPT] = "name",
    NULL
    };

8. 常用格式

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
66
67
68
69
70
71
static int parse_cmd(int argc, char **argv)
{
int opt; // getopt_long() 每次的返回值,一般让长选项返回0,短选项返回各自的值
int long_opt; // getopt_long() 每次的返回实际选项中的 option->val
int long_opt_idx; // getopt_long() 每次的返回的实际选项在 longopts 中的index
// 这个enum方便标记option中的val
enum {
O_DEV,
O_ALG,
O_IMPORT,
};
// 这里是实际选项
struct option long_opts[] = {
{"dev", required_argument, &long_opt, O_DEV},
{"alg", required_argument, &long_opt, O_ALG},
{"import", no_argument, &long_opt, O_IMPORT},
{0, 0, 0, 0},
};
// 可以设置一部分短选项
char *optstr = "a:b:";
}

while ((opt = getopt_long(argc, argv, optstr, long_opts, &long_opt_idx)) != -1)
{
switch (opt) {
case 0: // option->flag 不为NULL时,getopt_long() 返回0,同时会在long_opt中返回 option->val
// option->flag 为NULL 时,getopt_long() 返回 option->val 的值,这时候需要跟短选项的值区分开
switch (long_opt)
{
case O_DEV:
mc.dev = atoi(optarg);
break;

case O_ALG:
if (strcasecmp(optarg, "rsa") == 0) {
mc.algo = XCRYPTO_ALGO_RSA;
mc.opt = OPT_RSA;
} else if (strcasecmp(optarg, "sm1") == 0) {
mc.algo = XCRYPTO_ALGO_SM1;
mc.opt = OPT_SM1;
} else if (strcasecmp(optarg, "sm2") == 0) {
mc.algo = XCRYPTO_ALGO_SM2;
mc.opt = OPT_SM2;
} else if (strcasecmp(optarg, "sm3") == 0) {
mc.algo = XCRYPTO_ALGO_SM3;
mc.opt = OPT_SM3;
} else if (strcasecmp(optarg, "sm4") == 0) {
mc.algo = XCRYPTO_ALGO_SM4;
mc.opt = OPT_SM4;
} else {
xb_log_error("unsupport alg: %s", optarg);
err = 1;
}
break;

case O_IMPORT:
mc.inout = 1;
break;

// 这里是短选项的值
case 'a':
xb_log_debug("====>ind %d, %s, %s", optind, argv[optind], optarg);
break;
case 'b':
xb_log_debug("====>ind %d, %s, %s", optind, argv[optind], optarg);
break;
default:
err = 1;
break;
}
}

7. 参考

  1. man 3 getopt
  2. man 3 getsubopt