处理命令行参数的设计模式是什么

时间:2008-09-10 15:33:50

标签: design-patterns command-line arguments language-agnostic

如果您正在编写可从命令行执行的程序,您通常希望为用户提供多个选项或标志,以及可能的多个参数。我偶然发现了这么多次,但是有没有某种设计模式可以循环遍历args并调用适当的处理函数?

考虑:

myprogram -f filename -d directory -r regex

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

16 个答案:

答案 0 :(得分:15)

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

您应该考虑应用模板模式(“设计模式”[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()视为适合参数处理(预处理)的部分。

希望这有帮助。

答案 1 :(得分:14)

我不知道任何有关处理的文件“模式”。

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

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

答案 2 :(得分:5)

你没有提到这种语言,但对于Java我们很喜欢Apache Commons CLI。对于C / C ++,getopt。

答案 3 :(得分:4)

对此有一些评论...

首先,虽然本身没有任何模式,但编写解析器本质上是一种机械练习,因为给定语法,可以很容易地生成解析器。我想到了像Bison和ANTLR这样的工具。

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

我为C ++写了一篇文章,它节省了许多getopt赋予的功能并充分利用了模板:TCLAP

答案 4 :(得分:4)

嗯,这是一个老帖子,但我还是想贡献一下。这个问题的目的是选择设计模式,但我可以看到很多关于使用哪个库的讨论。我已经检查了lindsay的microsoft链接,它讨论了要使用的模板设计模式。

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

我宁愿选择" Command"设计模式。此模式最适合菜单驱动选项。

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

所以在你的情况下,-f,-d和-r都变成了具有公共或单独接收器定义的命令。这样,将来可以定义更多的接收器。下一步将链接这些命令的责任,以防需要处理链。我会选择。

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

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

答案 5 :(得分:2)

如果您使用C ++并且可以使用Boost,那么boost::program_options库很不错。

答案 6 :(得分:2)

假设您有一个“config”对象,您希望使用标志和一个合适的命令行解析器进行设置,该解析器负责解析命令行并提供一个常量的选项流,这里是一个伪代码块

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;
    }
}

答案 7 :(得分: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);
   }

答案 8 :(得分:2)

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

答案 9 :(得分:1)

我建议使用命令行处理器库。 Some Russian guy创造了一个体面的,但其中有很多。将节省您一些时间,以便您可以专注于应用程序的目的,而不是解析命令行开关!

答案 10 :(得分:1)

答案 11 :(得分:1)

答案 12 :(得分:0)

你没有提到这种语言,但如果你正在寻找一个非常好的Objective-C包装器,那么Dave Dribin的DDCLI框架非常好。

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

答案 13 :(得分:0)

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

希望这有帮助

答案 14 :(得分:0)

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

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

This文章更详细地讨论了它。

答案 15 :(得分:-1)

我对图书馆并不感兴趣,但这绝对有帮助。我正在寻找一些“伪代码”,它说明了处理你的平均标志和一堆较长的参数,作为一个例子。