0%

周谈(23)-撸了一个轻量级的C语言命令行库

今天把前段时间在项目中写的一个命令框架整理一下,剥离出来独立成一个动态库。

原理

基于getopt实现了一个简易的命令行库,目前只支持长选项的功能。编译使用的cmake工具。

主要的功能:
1、支持自定义选项、自定义解析函数
2、支持显示内部流程日志
3、封装成动态库 编译调用

没有很深奥难懂的知识点。主要还是为了方便使用吧。下面讲一下实现。

实现

选项配置

每一个选项的配置对应一个结构体:

1
2
3
4
5
6
7
/* 命令选项信息及回调函数 */
typedef struct {
const char *name;
const char *help;
cmd_opt_help_printer help_fn;
cmd_opt_parser parser_fn;
} cmd_opt_cfg;

对应帮助信息、选项名称还有就是解析函数。当help为NULL时,帮助信息可以让开发者自己输出,主要是考虑有些帮助信息是动态生成的,跟环境相关,不是固定的。

所有的命令选项配置,最终会被存放到内部的全局变量中,对应结构如下:

1
2
3
4
5
6
7
8
9
10
11

typedef struct {
const char *prog_name;
/* 所有选项的配置信息 */
cmd_opt_cfg *opt_cfgs;

/* 用于getopt_long的选项配置,最后一个需要是NULL */
struct option *long_opts;
uint32_t opt_cnt; /* 选项个数 */
uint32_t max_opt_num;
} cmd_opt_configs;

帮助信息

封装了一个函数usage,输出帮助信息

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23

void usage(void)
{
printf("Usage\n"
" %s [options] ...\n"
"Options:\r\n",
g_cmd_opt_configs->prog_name);


uint32_t i = 0;
for (; i < g_cmd_opt_configs->opt_cnt; i++)
{
if ( g_cmd_opt_configs->opt_cfgs[i].help)
{
printf("%s", g_cmd_opt_configs->opt_cfgs[i].help);
}
else if ( g_cmd_opt_configs->opt_cfgs[i].help_fn)
{
g_cmd_opt_configs->opt_cfgs[i].help_fn( g_cmd_opt_configs->opt_cfgs[i].name);
}
}
}

命令解析

命令解析是最核心的内容,调用getopt_long来解析参数,如果返回为0表示匹配了选项,然后根据返回的opt_idx索引到具体的选项,最后调用对应的选项解析函数。

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
int cmd_parse(void *results, const int argc, const char **argv)
{
int opt, retval, opt_idx;
int ret = 0;

g_cmd_opt_configs->prog_name = argv[0];

while ((opt = getopt_long(argc, (char * const*)argv, "", g_cmd_opt_configs->long_opts, &opt_idx)) != EOF)
{
switch (opt)
{
case 0:
retval = cmd_parse_opt_with_idx(opt_idx, results);
if (retval != 0)
{
usage();
return retval;
}
break;

default: // hit unknown option
if (!__command_unknown_option_bypass())
{
usage();
exit(EXIT_FAILURE);
}
break;
}
}

return 0;
}

代码已经上传到gitee中,感兴趣可以去看看,欢迎交流,提提意见。

https://gitee.com/fishmwei/ccmd

在源码中带有一个example, 运行效果如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
root@keep-VirtualBox:/media/sf_VM_SHARE/ccmd/example/build# ./ccmd_test --help
Usage
./ccmd_test [options] ...
Options:
--help: prints this help
--name: specify student name
--address: specify student home address
--age: specify student age
--fresh: is fresh graduate



root@keep-VirtualBox:/media/sf_VM_SHARE/ccmd/example/build# ./ccmd_test --name xiaoming --address Fuzhou,Fujian --age 18 --fresh
get student info:
name : xiaoming
address: Fuzhou,Fujian
age : 18
fresh : true

更多

端午节废了一天,今天赶紧振作起来,搞点事情。

这段时间,工作上也算是有点儿忙吧,家里也出了些烦心事,又落下了一周文章。


行动,才不会被动!

欢迎关注个人公众号 微信 -> 搜索 -> fishmwei,沟通交流。

博客地址: https://fishmwei.github.io/

掘金主页: https://juejin.cn/user/2084329776486919