如何使用Boost.Program_options实现子命令?

时间:2022-09-08 23:48:05

I'd like to implement subcommands to my program. I also need the ability to have different argument options for different subcommands. What's the best way to do this using Boost.Program_options?

我想为我的程序实现子命令。我还需要能够为不同的子命令设置不同的参数选项。使用Boost.Program_options执行此操作的最佳方法是什么?

Subcommands are used in programs like svn, git and apt-get.

子命令用于svn,git和apt-get等程序。

For example in GIT some of the available subcommands are:

例如,在GIT中,一些可用的子命令是:

git status  
git push  
git add  
git pull  

My question is basically the same as this guy's: http://boost.2283326.n4.nabble.com/subcommands-with-program-options-like-svn-command-td2585537.html

我的问题与这个人的问题基本相同:http://boost.2283326.n4.nabble.com/subcommands-with-program-options-like-svn-command-td2585537.html

2 个解决方案

#1


41  

If I understand the problem correctly, you want to parse command line options of the following form:

如果我正确理解了问题,您需要解析以下格式的命令行选项:

[--generic-option ...] cmd [--cmd-specific-option ... ] 

Here is my example solution. For clarity I'm going to omit any validation code, but hopefully you can see how it would be added fairly simply.

这是我的示例解决方案。为清楚起见,我将省略任何验证代码,但希望你能看到如何简单地添加它。

In this example, we have the "ls" subcommand, and possibly others. Each subcommand has some specific options, and in addition there are generic options. So let's start by parsing the generic options and the command name.

在这个例子中,我们有“ls”子命令,可能还有其他子命令。每个子命令都有一些特定选项,此外还有通用选项。因此,让我们从解析通用选项和命令名称开始。

po::options_description global("Global options");
global.add_options()
    ("debug", "Turn on debug output")
    ("command", po::value<std::string>(), "command to execute")
    ("subargs", po::value<std::vector<std::string> >(), "Arguments for command");

po::positional_options_description pos;
pos.add("command", 1).
    add("subargs", -1);

po::variables_map vm;

po::parsed_options parsed = po::command_line_parser(argc, argv).
    options(global).
    positional(pos).
    allow_unregistered().
    run();

po::store(parsed, vm);

Notice that we've created a single positional option for the command name, and multiple positional options for the command options.

请注意,我们为命令名创建了一个位置选项,并为命令选项创建了多个位置选项。

Now we branch on the relevant command name and re-parse. Instead of passing in the original argc and argv we now pass in the unrecognized options, in the form of an array of strings. The collect_unrecognized function can provide this - all we have to do is remove the (positional) command name and re-parse with the relevant options_description.

现在我们分支相关的命令名并重新解析。我们现在以字符串数组的形式传入无法识别的选项,而不是传入原始的argc和argv。 collect_unrecognized函数可以提供这一点 - 我们所要做的就是删除(位置)命令名称并使用相关的options_description重新解析。

std::string cmd = vm["command"].as<std::string>();
if (cmd == "ls")
{
    // ls command has the following options:
    po::options_description ls_desc("ls options");
    ls_desc.add_options()
        ("hidden", "Show hidden files")
        ("path", po::value<std::string>(), "Path to list");

    // Collect all the unrecognized options from the first pass. This will include the
    // (positional) command name, so we need to erase that.
    std::vector<std::string> opts = po::collect_unrecognized(parsed.options, po::include_positional);
    opts.erase(opts.begin());

    // Parse again...
    po::store(po::command_line_parser(opts).options(ls_desc).run(), vm);

Note that we used the same variables_map for the command-specific options as for the generic ones. From this we can perform the relevant actions.

请注意,我们对命令特定选项使用与通用选项相同的variables_map。由此我们可以执行相关操作。

The code fragments here are taken from a compilable source file which includes some unit tests. You can find it on gist here. Please feel free to download and play with it.

这里的代码片段取自可编译的源文件,其中包括一些单元测试。你可以在这里找到它。请随时下载并使用它。

#2


3  

You can take the subcommand name off the command line using positional options - see this tutorial.

您可以使用位置选项从命令行中取出子命令名称 - 请参阅本教程。

There doesn't seem to be any built-in support for subcommands - you will need to set the allow_unregistered option on the top-level parser, find the command name, then run it through a second parser to get any subcommand-specific options.

似乎没有对子命令的任何内置支持 - 您需要在*解析器上设置allow_unregistered选项,找到命令名称,然后通过第二个解析器运行它以获取任何子命令特定的选项。

#1


41  

If I understand the problem correctly, you want to parse command line options of the following form:

如果我正确理解了问题,您需要解析以下格式的命令行选项:

[--generic-option ...] cmd [--cmd-specific-option ... ] 

Here is my example solution. For clarity I'm going to omit any validation code, but hopefully you can see how it would be added fairly simply.

这是我的示例解决方案。为清楚起见,我将省略任何验证代码,但希望你能看到如何简单地添加它。

In this example, we have the "ls" subcommand, and possibly others. Each subcommand has some specific options, and in addition there are generic options. So let's start by parsing the generic options and the command name.

在这个例子中,我们有“ls”子命令,可能还有其他子命令。每个子命令都有一些特定选项,此外还有通用选项。因此,让我们从解析通用选项和命令名称开始。

po::options_description global("Global options");
global.add_options()
    ("debug", "Turn on debug output")
    ("command", po::value<std::string>(), "command to execute")
    ("subargs", po::value<std::vector<std::string> >(), "Arguments for command");

po::positional_options_description pos;
pos.add("command", 1).
    add("subargs", -1);

po::variables_map vm;

po::parsed_options parsed = po::command_line_parser(argc, argv).
    options(global).
    positional(pos).
    allow_unregistered().
    run();

po::store(parsed, vm);

Notice that we've created a single positional option for the command name, and multiple positional options for the command options.

请注意,我们为命令名创建了一个位置选项,并为命令选项创建了多个位置选项。

Now we branch on the relevant command name and re-parse. Instead of passing in the original argc and argv we now pass in the unrecognized options, in the form of an array of strings. The collect_unrecognized function can provide this - all we have to do is remove the (positional) command name and re-parse with the relevant options_description.

现在我们分支相关的命令名并重新解析。我们现在以字符串数组的形式传入无法识别的选项,而不是传入原始的argc和argv。 collect_unrecognized函数可以提供这一点 - 我们所要做的就是删除(位置)命令名称并使用相关的options_description重新解析。

std::string cmd = vm["command"].as<std::string>();
if (cmd == "ls")
{
    // ls command has the following options:
    po::options_description ls_desc("ls options");
    ls_desc.add_options()
        ("hidden", "Show hidden files")
        ("path", po::value<std::string>(), "Path to list");

    // Collect all the unrecognized options from the first pass. This will include the
    // (positional) command name, so we need to erase that.
    std::vector<std::string> opts = po::collect_unrecognized(parsed.options, po::include_positional);
    opts.erase(opts.begin());

    // Parse again...
    po::store(po::command_line_parser(opts).options(ls_desc).run(), vm);

Note that we used the same variables_map for the command-specific options as for the generic ones. From this we can perform the relevant actions.

请注意,我们对命令特定选项使用与通用选项相同的variables_map。由此我们可以执行相关操作。

The code fragments here are taken from a compilable source file which includes some unit tests. You can find it on gist here. Please feel free to download and play with it.

这里的代码片段取自可编译的源文件,其中包括一些单元测试。你可以在这里找到它。请随时下载并使用它。

#2


3  

You can take the subcommand name off the command line using positional options - see this tutorial.

您可以使用位置选项从命令行中取出子命令名称 - 请参阅本教程。

There doesn't seem to be any built-in support for subcommands - you will need to set the allow_unregistered option on the top-level parser, find the command name, then run it through a second parser to get any subcommand-specific options.

似乎没有对子命令的任何内置支持 - 您需要在*解析器上设置allow_unregistered选项,找到命令名称,然后通过第二个解析器运行它以获取任何子命令特定的选项。