Table of Contents:

linux下getopt()方法

函数原型

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

optstring选项字符串格式:

`a:b:cd::e`,这就是一个选项字符串。
对应到命令行就是-a ,-b ,-c ,-d, -e 。
冒号又是什么呢?
冒号表示参数,一个冒号就表示这个选项后面必须带有参数(没有带参数会报错哦),但是这个参数可以和选项连在一起写,也可以用空格隔开,比如-a123 和-a   123(中间有空格) 都表示123是-a的参数;
两个冒号的就表示这个选项的参数是可选的,即可以有参数,也可以没有参数,但要注意有参数时,参数与选项之间不能有空格(有空格会报错的哦),这一点和一个冒号时是有区别的。

参考链接

boost::program_options

命令行的处理工具。
主要通过三个组件完成:
options_description(选项描述器) 描述当前的程序定义了哪些选项
parse_command_line(选项分析器) 解析由命令行输入的参数
variables_map(选项存储器) 容器,用于存储解析后的选项

CLI11

CLI11是一个基于C++开发的命令行解析库
使用很方便,只需要#include <CLI11.hpp>,当然也可以使用cmake编译版本

一个简单的例子

#include "CLI11.hpp"
#include <iostream>

//只接受一个可选参数-p
int main(int argc, char **argv) {
    CLI::App app{"App description"};

    // Define options
    int p = 0;
    app.add_option("-p", p, "Parameter");

    CLI11_PARSE(app, argc, argv);

    std::cout << "Parameter value: " << p << std::endl;
    return 0;
}

CLI::App 是与库的所有交互的基础
CLI11_PARSE 宏内部执行app.parse(argc,argv)对命令行参数解析,出错时抛出ParseError,然后捕获异常,打印错误信息并退出程序

主要功能

  1. 位置参数(Positional Arguments): 位置参数是指在命令行中按照特定的顺序传递给命令的参数。它们通常用于指定命令需要操作的主要对象或实体。位置参数的顺序很重要,因为命令会根据它们的位置来解释参数的含义。例如,在以下命令中:cp source_file destination_file
    这里的source_filedestination_file就是位置参数,它们分别指定了源文件和目标文件的路径。
  2. 标志(Flags): 标志通常是以短横线(-)或双短横线(--)开头的参数,用于在命令行中传递额外的选项和信息。标志是可选的,它们通常用于启用或禁用特定功能,或者设置命令的行为。标志没有固定的顺序,因为它们是通过标识符来区分的。例如,在以下命令中:
    python -v
  3. 选项(Options): 选项是标志的一种特殊形式,通常用于传递具有关联值的参数。选项提供了一种在命令行中指定具体配置或设置的方式。选项的格式通常是--option=value--option value,具体取决于命令的要求。例如,在以下命令中:
    python script.py --input-file=input.txt --output-file=output.txt
    这里的--input-file--output-file是选项,分别传递了输入文件和输出文件的路径。

subcommand 子命令

子命令就是包含了一系列选项的一个关键字,如git commit/clone 这里面的commit clone后面还可以跟各种选项,他们就是git程序的子命令
子命令的类类型和App相同,因此可以任意嵌套

实例

//把CLI11.hpp放到当前目录下
#include "CLI11.hpp"
#include <iostream>
using namespace std;

int main(int argc, char **argv) {
    CLI::App app{"App description"}; // 软件描述出现在第一行打印
    app.footer("My footer"); // 最后一行打印
    app.get_formatter()->column_width(40); // 列的宽度
    app.require_subcommand(1); // 表示运行命令需要且仅需要一个子命令


    auto sub1 = app.add_subcommand("sub1""subcommand1");
    auto sub2 = app.add_subcommand("sub2""subcommand1");
    sub1->fallthrough(); // 当出现的参数子命令解析不了时,返回上一级尝试解析
    sub2->fallthrough();


    // 定义需要用到的参数
    string filename;
    int threads = 10;
    int mode = 0;
    vector<int> barcodes;
    bool reverse = false;
    string outPath;
    if (sub1)
    {
        // 第一个参数不加-, 表示位置参数,位置参数按出现的顺序来解析
        // 这里还检查了文件是否存在,已经是必须参数
        sub1->add_option("file", filename, "Position paramter")->check(CLI::ExistingFile)->required();


        // 检查参数必须大于0
        sub1->add_option("-n,-N", threads, "Set thread number")->check(CLI::PositiveNumber);
    }
    if (sub2)
    {
        // 设置范围
        sub2->add_option("-e,-E", mode, "Set mode")->check(CLI::Range(0,3));
        // 将数据放到vector中,并限制可接受的长度
        sub2->add_option("-b", barcodes, "Barcodes info:start,len,mismatch")->expected(3,6);
    }
    // 添加flag,有就是true
    app.add_flag("-r,-R", reverse, "Apply reverse");
    // 检查目录是否存在
    app.add_option("-o", outPath, "Output path")->check(CLI::ExistingDirectory);


    CLI11_PARSE(app, argc, argv);


    // 判断哪个子命令被使用
    if (sub1->parsed())
    {
        cout<<"Got sub1"<<endl;
        cout<<"filename:"<<filename<<endl;
        cout<<"threads:"<<threads<<endl;
    }
    else if (sub2->parsed())
    {
        cout<<"Got sub2"<<endl;
        cout<<"mode:"<<mode<<endl;
        cout<<"barcodes:";
        for (auto& b : barcodes)
            cout<<b<<" ";
        cout<<endl;
    }
    cout<<endl<<"Comman paras"<<endl;
    cout<<"reverse:"<<reverse<<endl;
    cout<<"outPath:"<<outPath<<endl;


    return 0;
}