撸了一个轻量级的C语言命令行库

165 阅读2分钟

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

原理

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

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

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

实现

选项配置

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

/* 命令选项信息及回调函数 */
typedef struct {
    const char *name;
    const char *help;
    cmd_opt_help_printer help_fn;
    cmd_opt_parser parser_fn;
} cmd_opt_cfg;

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

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


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,输出帮助信息


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索引到具体的选项,最后调用对应的选项解析函数。

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, 运行效果如下:

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,沟通交流。

博客地址: fishmwei.github.io/

掘金主页: juejin.cn/user/208432…