正在分析多个命令行开关

Parsing multiple command line switches

本文关键字:命令行 开关      更新时间:2023-10-16

我正在开发一个程序,该程序将扫描目录中的文件信息。将有一些可选的开关,用户可以将其放入其中,以扫描某些文件组,或以某些方式扫描,或以特定方式输出。

例如,如果用户输入:filesum -rRc,这将执行递归扫描,按升序输出文件,并且只扫描与C++相关的文件扩展名(.cpp|.h|.hpp)。

我如何以最干净的方式做到这一点?我研究过getopt,但我根本不了解它的实现,很难用文档找到好的例子。

我曾想过将开关输入的命令行参数转换为字符串并进行解析,但这难道不需要多个if语句来查看参数中是否只有一个开关,或者是否可以执行以下操作,如-rc吗?或者,我也在考虑根据输入的开关使用不同标志的while循环,但如果有更干净、更简单的方法,我将非常感谢任何建议。谢谢

如果可能的话,我宁愿不使用任何第三方库。

最干净的方法是使用Boost program_options

很有趣,我现在正在做这件事。我在这里只描述一下大概的想法。完整的代码目前超过500行,并使用更多的实用程序进行格式化等

作为一个例子,我正在制作程序help(它是相当伪的)。一个普通结构包含程序所需的所有选项,以及默认值:

struct help_opt
{
    string owner;
    string path;
    bool rec;
    int depth;
    help_opt() : owner(""), path("/"), rec(false), depth(0) { }
};

另一个结构为命令行参数建模:

struct help_args : public help_opt, public arguments <help_args>
{
    template <typename S>
    void define(S& s)
    {
        set(s, owner,   "owner",    'w',  "force <name> as owner",         "name");
        set(s, path,    "output",   'o',  "save all output to <path>",     "path");
        set(s, rec,     "recurse",  'r',  "recurse into sub-topics");
        set(s, depth,   "depth",    'd',  "set <number> as maximum depth", "number");
    }
    string title() const { return "my help"; }
    string usage() const { return "help [<options>] [<topic>]"; }
    string info() const
    {
        return
            "Display help on <topic>, "
            "or list available topics if no <topic> is given.";
    }
    help_args(int argc, char* argv[]) : arguments(argc, argv) { }
};

其中类arguments是我所需要的所有基础设施。所有参数以及元数据(名称、缩写等)都在define()中定义,每个参数都通过调用方法set()来定义。

参数s用于支持多项操作,例如收集用户输入、构建帮助文本或显示用户给定的值。然后,我们将引用传递给每个成员,根据用户输入,这些成员可能会得到一个适当的值,或者保留默认值。然后是参数名称、缩写、完整帮助文本以及可选的参数名称。布尔参数被单独视为标志,不包含任何参数。

附加方法title()info()等指定程序自定义的信息消息。

实际处理由arguments的构造函数完成。这调用help_args中的define(),而CCD_13又调用arguments中的CCD14。对于每个选项,都会扫描命令行输入,并根据需要更新变量。同时,收集元数据并自动为--help--usage--version等选项生成输出,就像gnu程序中一样。

help_args作为模板参数传递给arguments的事实是允许从基类调用define(),即使它是一个模板,因此不是虚拟的。如果只需要一个操作,那么我们可以切换回纯虚拟方法。

我希望这是干净的。我知道有很多可用的工具和库,但我真的更喜欢这种方式。我可以在准备好的时候分享实现,它几乎完成了。

我检查了gnucat如何处理命令行选项。我在C中写了一个例子。你可以传递标志,比如:cli-ab file或cli-b-ac file或cli-bc file-a

#include <stdio.h>
int main (int argc, char *argv) {
    int c;
    while ((c = getopt(argc, argv, "abc")) != -1) {
        switch (c) {
            case 'a':
                printf("selected: an");
                break;
            case 'b':
                printf("selected: bn");
                break;
            case 'c':
                printf("selected: cn");
                break;
            default:
                printf("Usage: cli -a/-b/-cn");
                break;
        }
    }
}