2012-04-15 18 views
8

So che questa domanda è stata già posta, ma sto cercando un pars parser Java con una funzionalità specifica. Voglio che sia in grado di definire l'albero della riga di comando, quindi utilizzando i sottocomandi (e più di una profondità di livello). Quindi posso avere 3-4 livelli di comandi prima di arrivare alle opzioni. E questi sottocomandi si escludono a vicenda. GrazieJava CLI Parser

+0

Puoi pubblicare alcuni esempi di comandi che intendi analizzare? – dash1e

+1

comando secondario sub-comando secondario -opzione1 argomento -opzione2 argomento – Sophie

risposta

0

Se la vostra espressione di comando è complessa, allora si può pensare di definire la sintassi, scrivere le sue librerie BNF e utilizzare come JavaCC o AntLR per creare il proprio parser.

12

Può essere eseguito con JCommander. Ogni oggetto JCommander è, nella sua essenza, un comando con numero arbitrario di parametri e/o numero arbitrario di sottocomandi nidificati, dove l'oggetto top JCommander è il comando radice radice. I parametri di comando sono sempre specifici del comando per cui sono stati dichiarati e non interferiscono con i parametri di altri comandi. L'interfaccia per l'aggiunta di un sub-comando non è molto intuitivo, ma è possibile (vedere il metodo addCommand())

Ecco una classe di test proof-of-concept:

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

Edit: Come riutilizzare i comandi.

Soluzione 1, utilizzare l'ereditarietà:

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

penso che l'utilizzo e il comportamento è faily ovvia per questo. L'unico inconveniente è che è possibile estendere solo da una classe, che potrebbe limitare la riutilizzabilità in futuro.

Soluzione 2, usando il modello della composizione (vedere 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(); 
    } 

Qui, i parametri annidati commonArgs classi verranno trattati come se fossero parametri diretti della classe comando. Puoi aggiungere tutti i delegati che desideri o persino annidare i delegati all'interno di altri delegati e così via. Per ottenere il valore dell'opzione delegata dopo l'analisi, è sufficiente fare myCommand1.commonArgs.isCommonOpt, ecc.

+0

Ciao. In realtà gioco con JCommander tutto il giorno. Ho pochi problemi con esso. Le eccezioni del parser non sono chiare, non posso introdurle all'utente. Un'altra cosa è che ho bisogno di creare una command class per comando (e ne ho molti), non posso riutilizzarli perché le annotazioni non sono generiche. Oh e l'uso() non stampa realmente tutti i sottocomandi e le opzioni. – Sophie

+2

Probabilmente JCommander non è stato progettato con i sottocomandi come priorità assoluta, è più di una funzione * nascosta *, se lo si desidera. Non ci sono molti casi eccezionali, quindi puoi probabilmente racchiuderli in qualcosa di più utile. Per quanto riguarda 'usage()' sembra che non ci sia il supporto ufficiale dei sottocomandi, quindi dovrete fare un po 'di hacking, o semplicemente scrivere a mano. Non posso essere d'accordo sul riutilizzo, però, la riusabilità è l'unica cosa che amo di JCommander. Potrei essere in grado di aiutarti se mi fornisci un esempio concreto. – rodion

+0

Ok. Grazie! Quindi, per esempio, ho 2 comandi con nomi diversi e descrizioni diverse, ma entrambi ottengono gli stessi parametri. quindi, per impostare la descrizione, devo avere diverse istanze di classe di comando anche se i pararetri all'interno della classe saranno uguali. – Sophie

0

Penso che sarebbe meglio suddividere questi comandi a più livelli in diversi strumenti CLI.

Invece di:
program cmd sub-cmd sub-sub-cmd -option1 argument -option2 argument

Aggiunge uno o due livelli di programmare nome:
program-cmd-sub-cmd sub-sub-cmd -option1 argument -option2 argument

parola esempio reale:
svn-add -q -N foo.c

Si potrebbe mantenere tutte le classi richieste a singolo JAR (e riutilizzali quanto vuoi), aggiungi solo alcuni punti di accesso "principali". Per l'analisi CLI di base, raccomando anche JCommander.

1

picocli supporta sottocomandi nidificati a profondità 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 
       ) 
     ); 

Potrebbe piacerti anche il suo utilizzo come aiuto con stili e colori ANSI.

Si noti che la guida all'utilizzo elenca i sottocomandi registrati oltre alle opzioni e ai parametri posizionali.

enter image description here

L'aiuto utilizzo è facilmente personalizzabile con annotazioni.

enter image description here

  • annotazione basata
  • sottocomandi git in stile
  • annidati sotto-sottocomandi
  • opzione parametri fortemente tipizzati
  • fortemente tipizzati parametri posizionali
  • tipo di conversione personalizzabile
  • opzioni multi-valore
  • modello intuitivo per quanti argomenti un campo consuma
  • API fluente
  • POSIX-style cluster opzioni brevi
  • opzioni lunghe
  • stile GNU
  • permette qualsiasi prefisso opzione
  • colori ANSI in help d'uso
  • aiuto d'uso personalizzabile
  • unico file sorgente: include come sorgente per mantenere l'applicazione un singolo jar