2012-04-15 13 views
8

Sé que esta pregunta ya se ha hecho, pero estoy buscando el analizador cli de Java con una funcionalidad específica. Quiero que sea capaz de definir el árbol de línea de comandos, utilizando subcomandos (y más de un nivel de profundidad). Así puedo tener 3-4 niveles de comandos antes de llegar a las opciones. Y estos subcomandos son mutuamente excluyentes. Gracias Java CLI Parser

+0

¿Puedes publicar algunos ejemplos de comandos que pretendes analizar? – dash1e

+1

subcomando comando sub-sub-comando -option1 argumento -option2 argumento – Sophie

Respuesta

0

Si su expresión comando es complejo entonces se puede pensar para definir la sintaxis, escribir sus bibliotecas BNF y uso como JavaCC o AntLR para crear su propio analizador.

12

Se puede hacer con JCommander. Cada objeto JCommander es, en esencia, un comando con un número arbitrario de parámetros y/o un número arbitrario de subcomandos anidados, donde el objeto superior JCommander es el root comando. Los parámetros de comando son siempre específicos del comando para el que han sido declarados y no interfieren con los parámetros de otros comandos. La interfaz para agregar un control secundario no es muy intuitivo, pero es posible (véase el método addCommand())

Aquí es una clase de prueba prueba de concepto:

public class Test{ 

@Test 
public void nestedSubCommandTest() { 
    GeneralOptions generalOpts = new GeneralOptions(); 
    JCommander jc = new JCommander(generalOpts); 

    Command command = new Command(); 
    JCommander jc_command = addCommand(jc, "command", command); 

    SubCommand1 subcommand1 = new SubCommand1(); 
    JCommander jc_subcommand1 = addCommand(jc_command, "subcommand1", 
      subcommand1); 

    SubCommand2 subcommand2 = new SubCommand2(); 
    JCommander jc_subcommand2 = addCommand(jc_subcommand1, "subcommand2", 
      subcommand2); 

    SubCommand3 subcommand3 = new SubCommand3(); 
    addCommand(jc_subcommand2, "subcommand3", subcommand3); 

    jc.parse("--general-opt", 
     "command", "--opt", 
     "subcommand1", 
     "subcommand2", "--sub-opt2", 
     "subcommand3", "--sub-opt3"); 

    assertTrue(generalOpts.opt);// --general-opt was set 
    assertTrue(command.opt);// command --opt was set 
    assertFalse(subcommand1.opt);// subcommand1 --sub-opt1 was not set 
    assertTrue(subcommand2.opt);// subcommand2 --sub-opt2 was set 
    assertTrue(subcommand3.opt);// subcommand3 --sub-opt3 was set 
} 

private static JCommander addCommand(JCommander parentCommand, 
     String commandName, Object commandObject) { 
    parentCommand.addCommand(commandName, commandObject); 
    return parentCommand.getCommands().get(commandName); 
} 

public static class GeneralOptions { 
    @Parameter(names = "--general-opt") 
    public boolean opt; 
} 

@Parameters 
public static class Command { 
    @Parameter(names = "--opt") 
    public boolean opt; 
} 

@Parameters 
public static class SubCommand1 { 
    @Parameter(names = "--sub-opt1") 
    public boolean opt; 
} 

@Parameters 
public static class SubCommand2 { 
    @Parameter(names = "--sub-opt2") 
    public boolean opt; 
} 

@Parameters 
public static class SubCommand3 { 
    @Parameter(names = "--sub-opt3") 
    public boolean opt; 
} 
} 

Editar: Cómo para reutilizar los comandos.

Solución 1, el uso de la herencia:

public class CommonArgs{ 
    @Parameter(names="--common-opt") 
    public boolean isCommonOpt; 
    } 

    @Parameters(description = "my command 1") 
    public class MyCommand1 extends CommonArgs{} 

    @Parameters(description = "my command 2") 
    public class MyCommand2 extends CommonArgs{} 

creo que el uso y el comportamiento es faily obvia para éste. El único inconveniente es que solo se puede extender desde una clase, que puede limitar la reutilización en el futuro.

Solución 2, utilizando el patrón composición (ver la doc here):

public class CommonArgs{ 
    @Parameter(names="--common-opt") 
    public boolean isCommonOpt; 
    } 

    @Parameters(description = "my command 1") 
    public class MyCommand1{ 
    @ParametersDelegate 
    public CommonArgs commonArgs = new CommonArgs(); 
    } 

    @Parameters(description = "my command 2") 
    public class MyCommand2{ 
    @ParametersDelegate 
    public CommonArgs commonArgs = new CommonArgs(); 
    } 

Aquí, los parámetros de los anidados commonArgs clases serán tratados como si fueran parámetros directos de la clase de comandos. Puede agregar tantos delegados como desee, o incluso anidar delegados dentro de otros delegados, y así sucesivamente. Para obtener el valor de la opción delegada después del análisis, simplemente haga myCommand1.commonArgs.isCommonOpt, etc.

+0

Hola. Estoy jugando con JCommander todo el día en realidad. Tengo algunos problemas con eso. Las excepciones del analizador no son claras en absoluto, no puedo presentarlas al usuario. Otra cosa es que necesito crear una clase de comando por comando (y tengo mucho), no puedo reutilizarlos porque las anotaciones no son genéricas. Ah, y el uso() realmente no imprime todos los subcomandos y opciones. – Sophie

+2

JCommander probablemente no fue diseñado con subcomandos como máxima prioridad, es más una característica * oculta *, si se quiere. No hay muchos casos excepcionales, por lo que probablemente pueda envolverlos en algo más útil. En cuanto a 'usage()', no parece que haya soporte oficial de subcomandos, por lo que tendrás que hacer algo de hackeo, o simplemente escribirlo a mano. No puedo estar de acuerdo con la reutilización, sin embargo, la reutilización es lo único que amo de JCommander. Quizás pueda ayudar con eso si me das un ejemplo concreto. – rodion

+0

Ok. ¡Gracias! Entonces, por ejemplo, tengo 2 comandos que tienen diferentes nombres y descripciones diferentes, pero ambos obtienen los mismos parámetros. entonces para establecer la descripción, debo tener diferentes instancias de clase de comando aunque los parareters dentro de la clase serán los mismos. – Sophie

0

Creo que será mejor dividir estos comandos de varios niveles en varias herramientas CLI.

En lugar de:
program cmd sub-cmd sub-sub-cmd -option1 argument -option2 argument

Anexar uno o dos niveles para programar nombre:
program-cmd-sub-cmd sub-sub-cmd -option1 argument -option2 argument

real palabra ejemplo:
svn-add -q -N foo.c

Se podría mantener todas las clases requeridas en un solo JAR (y reutilícelos tanto como desee), simplemente agregue varios puntos de entrada "principales". Para el análisis CLI básico también recomiendo JCommander.

1

picocli admite subcomandos anidados a profundidad arbitraria.

CommandLine commandLine = new CommandLine(new MainCommand()) 
     .addSubcommand("cmd1", new ChildCommand1()) // 1st level 
     .addSubcommand("cmd2", new ChildCommand2()) 
     .addSubcommand("cmd3", new CommandLine(new ChildCommand3()) // 2nd level 
       .addSubcommand("cmd3sub1", new GrandChild3Command1()) 
       .addSubcommand("cmd3sub2", new GrandChild3Command2()) 
       .addSubcommand("cmd3sub3", new CommandLine(new GrandChild3Command3()) // 3rd 
         .addSubcommand("cmd3sub3sub1", new GreatGrandChild3Command3_1()) 
         .addSubcommand("cmd3sub3sub2", new GreatGrandChild3Command3_2()) 
           // etc 
       ) 
     ); 

También le puede interesar su uso ayuda con los estilos y colores ANSI.

Tenga en cuenta que la ayuda de uso enumera los subcomandos registrados además de las opciones y los parámetros posicionales.

enter image description here

La ayuda de uso, se puede personalizar fácilmente con anotaciones.

enter image description here

  • basadas en anotación
  • subcomandos de estilo git
  • anidados sub-subcomandos
  • parámetros de opción inflexible de tipos
  • inflexible parámetros posicionales
  • conversión de tipo adaptable
  • Opciones de valores múltiples
  • modelo intuitivo para el número de argumentos un campo consume
  • API fluida
  • agrupado POSIX de estilo opciones cortas
  • opciones largas
  • estilo GNU
  • permite a cualquier opción de prefijo
  • colores ANSI en ayuda de uso
  • ayuda de uso personalizable
  • archivo de origen único: incluir como fuente para mantener su aplicación un solo contenedor