Java CLI Parser
Я знаю, что этот вопрос уже задавался, но я ищу Java-анализатор с определенной функциональностью. Я хочу, чтобы он мог определять дерево командной строки, используя при этом подкоманды (и глубину более одного уровня). Таким образом, у меня может быть 3-4 уровня команд, прежде чем я доберусь до опций. И эти подкоманды являются взаимоисключающими. Спасибо
4 ответа
Может быть сделано с JCommander. каждый JCommander
Объект по своей сути является командой с произвольным числом параметров и / или произвольным количеством вложенных подкоманд, где верх JCommander
Объект является корневой командой. Параметры команды всегда зависят от команды, для которой они были объявлены, и не влияют на параметры других команд. Интерфейс для добавления подкоманды не очень интуитивно понятен, но возможен (см. addCommand
() метод)
Вот тестовый класс для проверки концепции:
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;
}
}
Редактировать: Как повторно использовать команды.
Решение 1, используйте наследование:
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{}
Я думаю, что использование и поведение совершенно очевидно для этого. Единственным недостатком является то, что вы можете расширять только один класс, что может ограничить возможность повторного использования в будущем.
Решение 2, используя шаблон композиции (см. Документ здесь):
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();
}
Здесь вложенный commonArgs
параметры классов будут обрабатываться так, как если бы они были прямыми параметрами командного класса. Вы можете добавить столько делегатов, сколько пожелаете, или даже вкладывать делегатов в другие делегаты и так далее. Чтобы получить значение делегированной опции после разбора, просто выполните myCommand1.commonArgs.isCommonOpt
, так далее.
picocli поддерживает вложенные подкоманды на произвольную глубину.
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
)
);
Вам также может понравиться его использование со стилями и цветами ANSI.
Обратите внимание, что в справке по использованию перечислены зарегистрированные подкоманды в дополнение к опциям и позиционным параметрам.
Справка по использованию легко настраивается с помощью аннотаций.
- аннотации на основе
- подкоманды в стиле git
- вложенные подкоманды
- строго типизированные параметры
- строго типизированные позиционные параметры
- настраиваемое преобразование типов
- многозначные варианты
- интуитивно понятная модель для того, сколько аргументов потребляет поле
- свободный API
- Кластерные короткие опции в стиле POSIX
- Длинные варианты в стиле GNU
- позволяет любой префикс
- ANSI цвета в использовании помогают
- настраиваемая справка по использованию
- один исходный файл: включите в качестве источника, чтобы сохранить приложение в одной банке
Я думаю, что будет лучше разбить эту многоуровневую команду на несколько CLI-инструментов.
Вместо:program cmd sub-cmd sub-sub-cmd -option1 argument -option2 argument
Добавьте один или два уровня к имени программы:program-cmd-sub-cmd sub-sub-cmd -option1 argument -option2 argument
Пример реального слова:svn-add -q -N foo.c
Вы можете хранить все необходимые классы в одном JAR-файле (и использовать их сколько угодно), просто добавив несколько "основных" точек входа. Для базового CLI-разбора я также рекомендую JCommander
,