Pergunta

Se você estiver escrevendo um programa que seja executável a partir da linha de comando, geralmente desejará oferecer ao usuário diversas opções ou sinalizadores, junto com possivelmente mais de um argumento.Já tropecei nisso muitas vezes, mas existe algum tipo de padrão de design para percorrer argumentos e chamar as funções de manipulador apropriadas?

Considerar:

myprogram -f filename -d directory -r regex

Como você organiza as funções do manipulador depois de recuperar os argumentos usando quaisquer recursos internos do seu idioma?(respostas específicas do idioma são bem-vindas, se isso ajudar você a articular uma resposta)

Foi útil?

Solução

Não conheço nenhum "padrão" documentado para processamento.

Acredito que uma das bibliotecas/APIs mais antigas para lidar com argumentos é getopt.Pesquisar "getopt" no Google mostra muitas páginas de manual e links para implementações.

Geralmente, tenho um serviço de preferências ou configurações em meu aplicativo com o qual o processador de argumentos sabe como se comunicar.Os argumentos são então traduzidos em algo neste serviço que o aplicativo consulta.Isso pode ser tão simples quanto um dicionário de configurações (como uma configuração de string chamada "nome do arquivo").

Outras dicas

Acho que a resposta a seguir está mais na linha do que você está procurando:

Você deve aplicar o padrão de modelo (método de modelo em "Design Patterns" [Gamma, el al])

Resumindo, o processamento geral é assim:

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

Resumindo, implemente uma classe ConsoleEngineBase que possua métodos para:

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

Em seguida, crie um chassi que instancie uma instância ConsoleEngine() e envie a mensagem Main() para iniciá-la.

Para ver um bom exemplo de como aplicar isso a um console ou programa de linha de comando, verifique o seguinte link:http://msdn.microsoft.com/en-us/magazine/cc164014.aspx

O exemplo está em C#, mas as ideias são facilmente implementadas em qualquer outro ambiente.

Você consideraria GetOpt() apenas a parte que se encaixa no tratamento de argumentos (pré-processamento).

Espero que isto ajude.

Você não mencionou a linguagem, mas para Java nós adoramos CLI do Apache Commons.Para C/C++, getopt.

Alguns comentários sobre isso...

Primeiro, embora não existam padrões em si, escrever um analisador é essencialmente um exercício mecânico, uma vez que, dada uma gramática, um analisador pode ser facilmente gerado.Ferramentas como Bison e ANTLR vêm à mente.

Dito isto, os geradores de analisadores geralmente são um exagero para a linha de comando.Portanto, o padrão usual é escrever um você mesmo (como outros demonstraram) algumas vezes até ficar cansado de lidar com detalhes tediosos e encontrar uma biblioteca para fazer isso por você.

Eu escrevi um para C++ que economiza muito esforço que getopt transmite e faz bom uso de modelos: TCLAP

Bem, é um post antigo, mas ainda gostaria de contribuir.A questão era sobre a escolha de padrões de design, mas pude ver muita discussão sobre qual biblioteca usar.Eu verifiquei o link da Microsoft conforme Lindsay, que fala sobre o padrão de design do modelo a ser usado.

No entanto, não estou convencido com o post.A intenção do padrão de modelo é definir um modelo que será implementado por várias outras classes para ter um comportamento uniforme.Não acho que a análise da linha de comando se encaixe nisso.

Eu prefiro usar o padrão de design "Command".Este padrão é mais adequado para opções orientadas por menu.

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

então, no seu caso, -f, -d e -r se tornam comandos que possuem um receptor comum ou separado definido.Dessa forma, mais receptores poderão ser definidos no futuro.O próximo passo será encadear essas responsabilidades de comandos, caso seja necessária uma cadeia de processamento.Para o qual eu escolheria.

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

Acho que a combinação desses dois é melhor para organizar o código para processamento de linha de comando ou qualquer abordagem baseada em menu.

O impulso::program_options biblioteca é boa se você estiver em C++ e tiver o luxo de usar o Boost.

Supondo que você tenha um objeto "config" que deseja configurar com os flags e um analisador de linha de comando adequado que se encarrega de analisar a linha de comando e fornecer um fluxo constante de opções, aqui vai um bloco de pseudocódigo

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

Prefiro opções como "-t text" e "-i 44";Não gosto de "-fname" ou "--very-long-argument=some_value".

E "-?", "-h" e "/h" produzem uma tela de ajuda.

Esta é a aparência do meu código:

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

Estou analisando a resposta ANTLR do mes5k.Esse link para Codeproject é para um artigo que discute ANLTR e o uso do padrão de visita para implementar as ações que você deseja que seu aplicativo execute.Está bem escrito e vale a pena revisar.

Eu recomendaria usar uma biblioteca de processador de linha de comando. Algum cara russo criei um decente, mas existem muitos deles por aí.Você economizará algum tempo para que possa se concentrar no propósito do seu aplicativo, em vez de analisar as opções de linha de comando!

Getopt é o único caminho a percorrer.

http://sourceforge.net/projects/csharpoptparse

E quanto ao padrão do intérprete?http://www.dofactory.com/net/interpreter-design-pattern

Você não menciona uma linguagem para isso, mas se você está procurando um wrapper Objective-C realmente bom em torno do getopt, então a estrutura DDCLI de Dave Dribin é muito boa.

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

Eu uso o Obteropts::std e Getopts::longo em perl e também o Obter opção função em C.Isso padroniza a análise e o formato dos parâmetros.Outras linguagens possuem mecanismos diferentes para lidar com isso.

Espero que isto ajude

O design padrão geralmente segue o que getopt faz, existem bibliotecas getopt para muitas linguagens, .NET, python, C, Perl, PHP, etc.

O projeto básico é ter um analisador de linha de comando que retorne parte por parte os argumentos passados ​​para serem verificados em um loop.

Esse artigo discute isso com mais detalhes.

Não estou muito interessado nas bibliotecas, embora isso seja definitivamente útil.Eu estava procurando mais por algum "pseudocódigo" que ilustrasse o processamento de, digamos, um monte médio de sinalizadores e um monte de argumentos mais longos, por exemplo.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top