50

如果您正在编写一个可以从命令行执行的程序,您通常希望为用户提供多个选项或标志,以及可能不止一个参数。我已经绊倒了很多次,但是是否有某种设计模式用于循环 args 并调用适当的处理函数?

考虑:

myprogram -f filename -d directory -r regex

使用语言的任何内置函数检索参数后,如何组织处理函数?(欢迎使用特定语言的答案,如果这有助于您表达答案)

4

15 回答 15

17

我认为以下答案更符合您的要求:

您应该考虑应用模板模式(“设计模式”中的模板方法 [Gamma, el al])

简而言之,它的整体处理如下所示:

If the arguments to the program are valid then
    Do necessary pre-processing
    For every line in the input
        Do necessary input processing
    Do necessary post-processing
Otherwise
    Show the user a friendly usage message

简而言之,实现一个包含以下方法的 ConsoleEngineBase 类:

PreProcess()
ProcessLine()
PostProcess()
Usage()
Main()

然后创建一个机箱,实例化一个 ConsoleEngine() 实例并发送 Main() 消息以启动它。

要查看如何将其应用于控制台或命令行程序的一个很好的示例,请查看以下链接:http: //msdn.microsoft.com/en-us/magazine/cc164014.aspx

该示例使用 C# 编写,但这些想法很容易在任何其他环境中实现。

您会将 GetOpt() 视为适合参数处理(预处理)的部分。

希望这可以帮助。

于 2011-09-23T13:45:56.650 回答
14

我不知道任何记录在案的处理“模式”。

我相信用于处理参数的最古老的库/API 之一是 getopt。谷歌搜索“getopt”显示了许多手册页和实现的链接。

通常,我的应用程序中有一个首选项或设置服务,参数处理器知道如何与之通信。然后将参数转换为该服务中的应用程序然后查询的内容。这可以像设置字典一样简单(例如名为“文件名”的字符串设置)。

于 2008-09-10T15:41:59.793 回答
6

您没有提到该语言,但对于 Java,我们喜欢Apache Commons CLI。对于 C/C++,getopt。

于 2008-09-10T15:44:19.270 回答
5

好吧,它是一个旧帖子,但我仍然想贡献。该问题旨在选择设计模式,但是我可以看到很多关于使用哪个库的讨论。我已经按照 lindsay 查看了 microsoft 链接,该链接讨论了要使用的模板设计模式。

但是,我不相信这个职位。模板模式的目的是定义一个模板,该模板将由各种其他类实现以具有统一的行为。我不认为解析命令行适合它。

我宁愿选择“命令”设计模式。这种模式最适合菜单驱动的选项。

http://www.blackwasp.co.uk/Command.aspx

因此,在您的情况下,-f、-d 和 -r 都成为定义了通用或单独接收器的命令。这样以后可以定义更多的接收器。下一步将链接这些命令的职责,以防需要处理链。我会选择。

http://www.blackwasp.co.uk/ChainOfResponsibility.aspx

我想这两者的组合最好是组织命令行处理或任何菜单驱动方法的代码。

于 2014-05-10T16:04:36.897 回答
4

对此的一些评论...

首先,虽然本身没有任何模式,但编写解析器本质上是一种机械练习,因为给定语法,可以轻松生成解析器。想到了 Bison 和 ANTLR 等工具。

也就是说,解析器生成器对于命令行来说通常是多余的。所以通常的模式是自己写一个(正如其他人已经证明的那样)几次,直到你厌倦了处理繁琐的细节并找到一个库来为你做。

我为 C++ 编写了一个,它节省了 getopt 赋予的大量工作并很好地利用了模板:TCLAP

于 2008-09-16T22:39:52.187 回答
2

boost::program_options库非常适合使用C++ 并且可以使用 Boost。

于 2008-09-10T16:09:38.417 回答
2

假设您有一个“配置”对象,您打算使用标志和一个合适的命令行解析器来设置该对象,该解析器负责解析命令行并提供恒定的选项流,这里有一段伪代码

while (current_argument = cli_parser_next()) {
    switch(current_argument) {
        case "f": //Parser strips the dashes
        case "force":
            config->force = true;
            break;
        case "d":
        case "delete":
            config->delete = true;
            break;
        //So on and so forth
        default:
            printUsage();
            exit;
    }
}
于 2008-09-10T16:57:41.887 回答
2

我更喜欢“-t text”和“-i 44”之类的选项;我不喜欢“-fname”或“--very-long-argument=some_value”。

而“-?”、“-h”和“/h”都会产生帮助屏幕。

这是我的代码的外观:

int main (int argc, char *argv[])
   {  int i;
      char *Arg;
      int ParamX, ParamY;
      char *Text, *Primary;

   // Initialize...
   ParamX = 1;
   ParamY = 0;
   Text = NULL;
   Primary = NULL;

   // For each argument...
   for (i = 0; i < argc; i++)
      {
      // Get the next argument and see what it is
      Arg = argv[i];
      switch (Arg[0])
         {
         case '-':
         case '/':
            // It's an argument; which one?
            switch (Arg[1])
               {
               case '?':
               case 'h':
               case 'H':
                  // A cry for help
                  printf ("Usage:  whatever...\n\n");
                  return (0);
                  break;

               case 't':
               case 'T':
                  // Param T requires a value; is it there?
                  i++;
                  if (i >= argc)
                     {
                     printf ("Error:  missing value after '%s'.\n\n", Arg);
                     return (1);
                     }

                  // Just remember this
                  Text = Arg;

                  break;

               case 'x':
               case 'X':
                  // Param X requires a value; is it there?
                  i++;
                  if (i >= argc)
                     {
                     printf ("Error:  missing value after '%s'.\n\n", Arg);
                     return (1);
                     }

                  // The value is there; get it and convert it to an int (1..10)
                  Arg = argv[i];
                  ParamX = atoi (Arg);
                  if ((ParamX == 0) || (ParamX > 10))
                     {
                     printf ("Error:  invalid value for '%s'; must be between 1 and 10.\n\n", Arg);
                     return (1);
                     }

                  break;

               case 'y':
               case 'Y':
                  // Param Y doesn't expect a value after it
                  ParamY = 1;
                  break;

               default:
                  // Unexpected argument
                  printf ("Error:  unexpected parameter '%s'; type 'command -?' for help.\n\n", Arg);
                  return (1);
                  break;
               }

            break;

         default:
            // It's not a switch that begins with '-' or '/', so it's the primary option
            Primary = Arg;

            break;
         }
      }

   // Done
   return (0);
   }
于 2008-09-10T18:04:59.463 回答
2

我正在重复 mes5k 的 ANTLR 答案。此Codeproject 链接用于讨论 ANLTR 并使用访问模式来实现您希望应用程序执行的操作的文章。它写得很好,值得回顾。

于 2008-09-21T16:28:28.330 回答
1

我建议使用命令行处理器库。一些俄罗斯人创造了一个像样的,但那里有很多。将为您节省一些时间,以便您可以专注于应用程序的目的,而不是解析命令行开关!

于 2008-09-10T15:42:55.360 回答
1

Getopt 是唯一的方法。

http://sourceforge.net/projects/cshapoptparse

于 2008-09-10T15:51:41.063 回答
1

解释器模式怎么样? http://www.dofactory.com/net/interpreter-design-pattern

于 2016-07-28T16:12:48.183 回答
0

您没有为此提及一种语言,但如果您正在寻找一个非常好的围绕 getopt 的 Objective-C 包装器,那么 Dave Dribin 的 DDCLI 框架非常好。

http://www.dribin.org/dave/blog/archives/2008/04/29/ddcli

于 2008-09-10T15:47:21.307 回答
0

我在 perl 中使用Getopts::stdGetopts::long以及 C 中的Getopt函数。这标准化了参数的解析和格式。其他语言有不同的机制来处理这些。

希望这可以帮助

于 2008-09-10T15:47:22.260 回答
0

标准设计通常遵循 getopt 所做的,有许多语言的 getopt 库,.NET、python、C、Perl、PHP 等。

基本设计是有一个命令行解析器,它部分返回传递给循环检查的参数。

这篇文章更详细地讨论了它。

于 2008-09-10T15:47:32.243 回答