我目前正在尝试使用Spring Shell创建一个cli应用程序。
我希望用户能够快速选择2-3个选项中的一个。我目前的代码在eclipse中工作得很好但是当我在Powershell中启动它时,我必须多次输入(至少3次)才能选择该选项。按下输入一次后,没有任何反应。
我目前的方法:
@ShellMethod(key = { "setService", "select" }, value = "Choose a Speech to Text Service")
public void setService() {
boolean success = false;
do {
this.console.write("Please select a speech recognition service. Type in the number and press enter:");
this.console.write("1. Google Speech API");
this.console.write("2. Bing Speech API");
// Get Input
Scanner s = new Scanner(System.in);
String input = s.nextLine();
// Select Service
switch (input) {
case "1":
/*
* do something...
*/
console.write("Google Speech API selected");
success = true;
break;
case "2":
/*
* do something...
*/
console.write("Bing Speech API selected");
success = true;
break;
default:
this.console.error("Input not valid. Please type a number and press Enter to select a Service");
break;
}
} while (!success);
}
我如何解决PowerShell的问题,还是有更优雅的方式来执行此输入?
答案 0 :(得分:1)
面对类似的需求,并且没有在JLine 3或Spring-Shell 2文档的任何地方找到这样的例子,我做了一些实验...
基本上-这些组件并非旨在以这种方式工作。完全不支持在@ShellMethod上下文中尝试获取其他用户输入。
您可能使用System.out
或System.in
进行的任何IO活动最终都会由JLine在“幕后”进行不同的处理,具体取决于您最初启动的外壳类型(因此观察到从PowerShell调用应用程序与从IDE内部启动应用程序时,尝试的行为会有所不同。如果从Windows上的GitBash启动,则能够获得所需的行为,但如果从CMD启动,则无法获得预期的行为。在一种情况下,我最终导致应用程序完全崩溃。
但是,我确实找到了一种提示输入的方法,该输入对GitBash和CMD都适用。该示例无法在普通的JLine中使用,但只要您使用的是Spring Boot自动配置(就可以使用Spring为您的Shell应用程序初始化的“ LineReader”实例),它就可以在Spring-Shell 2.0中使用。>
@Autowired
LineReader reader;
public String ask(String question) {
return this.reader.readLine("\n" + question + " > ");
}
@ShellMethod(key = { "setService", "select" }, value = "Choose a Speech to Text Service")
public void setService() {
boolean success = false;
do {
String question = "Please select a speech recognition service. Type in the number and press enter:"
+ "\n 1. Google Speech API"
+ "\n 2. Bing Speech API";
// Get Input
String input = this.ask(question);
// Select Service
switch (input) {
case "1":
/*
* do something...
*/
console.write("Google Speech API selected");
success = true;
break;
case "2":
/*
* do something...
*/
console.write("Bing Speech API selected");
success = true;
break;
default:
this.console.error("Input not valid. Please type a number and press Enter to select a Service");
break;
}
} while (!success);
}
答案 1 :(得分:1)
Spring Shell 2基于JLine 3。对于“少量选项”,我建议使用选项卡补全等功能。
(它可以在Powershell上运行,但是在Eclipse-Console中我从未获得任何制表符完成支持)。
这很容易:
import java.util.List;
import org.jline.reader.LineReader;
import org.jline.reader.LineReaderBuilder;
import org.jline.reader.impl.completer.StringsCompleter;
import org.jline.terminal.Terminal;
import org.springframework.context.annotation.Lazy;
import org.springframework.shell.standard.ShellComponent;
import org.springframework.shell.standard.ShellMethod;
@ShellComponent
public class CompleterInteractionCommand {
private final List<String> OPTIONS = List.of("create", "update", "delete");
private final Terminal terminal;
public CompleterInteractionCommand(@Lazy final Terminal terminal) {
this.terminal = terminal;
}
@ShellMethod
public String completeSelect() {
LineReader lineReader = LineReaderBuilder.builder()
.terminal(this.terminal)
.completer(new StringsCompleter(this.OPTIONS))
.build();
/* Important: This allows completion on an empty buffer, rather than inserting a tab! */
lineReader.unsetOpt(LineReader.Option.INSERT_TAB);
String desription = "select on of this options: " + OPTIONS + "\n"
+ " use TAB (twice) to select them\n";
String input = lineReader.readLine(desription + "input: ").trim();
return "you selected \"" + input + "\"";
}
}
答案 2 :(得分:0)
为此,我使用TextIO做类似的事情。更具体地说,我在read
上使用了[IntInputReader][2]
方法,根据其文档,它很方便:
读取类型为T的值。它反复提示用户输入该值,直到他们提供有效的输入字符串为止。
由于我在Spring Shell应用程序中多次需要此功能(供用户从不同类型的对象中进行选择),因此我编写了一个通用方法
为用户提供给定类型{@code T}的项目列表,并提示他们选择一个或多个。可选地,一旦选择完成,将向用户显示其选择的摘要以供确认。
不幸的是,StackOverflow正在对我的Javadoc造成破坏,所以我创建了Gist with the full documented method。
public <T> List<T> cliObjectSelector(List<T> items, boolean allowMultiple,
boolean requireConfirmation, @Nullable String customPromptMessage,
Function<T, String> f) throws OperationCancelledException {
if(items == null || items.isEmpty()) {
textIO.getTextTerminal().print("No items found. Cannot continue");
throw new OperationCancelledException("The provided list of items is empty");
}
String userPrompt = (customPromptMessage != null) ? customPromptMessage + ":\n" :
String.format("Please select one of the %d items from the list:\n", items.size());
List<String> optionsList = items.stream()
.map(item -> {
return String.format("[%d] - %s", items.indexOf(item), f.apply(item));
}).collect(Collectors.toList());
List<T> selectedItems = new ArrayList<>();
optionsList.add(0, userPrompt);
while(true) {
textIO.getTextTerminal().println(optionsList);
T selected = items.get(
textIO.newIntInputReader()
.withMinVal(0)
.withMaxVal(items.size() - 1)
.read("Option number"));
selectedItems.add(selected);
if(allowMultiple == false) {
break;
}
if( ! askYesNo("Select another item?")) {
break;
}
}
if(requireConfirmation == false) {
return selectedItems;
}
if(confirmSelection(selectedItems.stream()
.map(item -> f.apply(item))
.collect(Collectors.toList()))) {
return selectedItems;
} else {
throw new OperationCancelledException();
}
}
示例用法:
@ShellMethod(
key = "select-something",
value = "Select something")
public void select() {
Instance selected = cliObjectSelector(instances, false, requireConfirmation,
"Please select one of the " + instances.size() + " instances from the list",
instance -> instance.getX().getName()).get(0);
}