我正在用Java创建一个自定义shell。我已经添加了历史记录,以便当按下向上箭头时它会转到上一个命令,但向上箭头似乎不起作用
这是我的代码:
public class MyShell {
public static class JavaStringHistory
{
private List<String> history = new ArrayList<String>();
}
public static void main(String[] args) throws java.io.IOException {
JavaStringHistory javaStringHistory = new JavaStringHistory();
javaStringHistory.history.add("");
Integer indexOfHistory = 0;
String commandLine;
BufferedReader console = new BufferedReader
(new InputStreamReader(System.in));
//Break with Ctrl+C
while (true) {
//read the command
System.out.print("Shell>");
commandLine = console.readLine();
javaStringHistory.history.add(commandLine);
//if just a return, loop
if (commandLine.equals(""))
continue;
//history
if (commandLine.equals(KeyEvent.VK_UP))
{
System.out.println("up arrow");
}
//help command
if (commandLine.equals("help"))
{
System.out.println();
System.out.println();
System.out.println("Welcome to the shell");
System.out.println("Written by: Alex Frieden");
System.out.println("--------------------");
System.out.println();
System.out.println("Commands to use:");
System.out.println("1) cat");
System.out.println("2) exit");
System.out.println("3) clear");
System.out.println();
System.out.println();
System.out.println("---------------------");
System.out.println();
}
if (commandLine.equals("clear"))
{
for(int cls = 0; cls < 10; cls++ )
{
System.out.println();
}
}
if(commandLine.startsWith("cat"))
{
System.out.println("test");
//ProcessBuilder pb = new ProcessBuilder();
//pb = new ProcessBuilder(commandLine);
}
else
{
System.out.println("Incorrect Command");
}
if (commandLine.equals("exit"))
{
System.out.println("...Terminating the Virtual Machine");
System.out.println("...Done");
System.out.println("Please Close manually with Options > Close");
System.exit(0);
}
indexOfHistory++;
}
}
}
我得到的只是
Shell>^[[A
Incorrect Command
Shell>
有什么想法吗?
答案 0 :(得分:6)
您的方法存在一些问题:
BufferedReader.readLine
不是在shell中用于历史循环的明智选择,因为您希望shell立即对光标键做出反应而不强迫用户按Return或Enter。只有用户命令才需要读取整行。因此,您需要扫描每个单个字符或键码的键盘输入,并自行决定是否例如光标键(历史循环向上/向下,命令行内光标移动的左/右)或命令行编辑的删除/退格等。readLine
读取控制字符创建的文本字符串可能取决于操作系统,甚至可能取决于shell和字符集(UTF-8,ISO-8859-1,US ASCII等)在控制台上。readLine
,例如在Linux上我看到“^ [[A”用于向上光标的东西,在Windows上,光标键被传递到 cmd.exe 的内置命令历史记录功能。即您需要将控制台置于原始模式(行编辑旁路且不需要输入密钥),而不是熟化模式(需要使用Enter键进行行编辑)。无论如何,为了回答关于如何找出BufferedReader.readLine
生成的密钥代码的初步问题,实际上非常简单。只需将字节转储到控制台,如下所示:
commandLine = console.readLine();
System.out.println("Entered command text: " + commandLine);
System.out.print ("Entered command bytes: ");
for (byte b : commandLine.getBytes())
System.out.print(b + ", ");
System.out.println();
在Linux下,光标可能类似于“27,91,65”或只是“91,65”,具体取决于终端。光标向下以“66”结束而不是在我的系统上。所以你可以这样做:
public class MyShell {
private static final String UP_ARROW_1 = new String(new byte[] {91, 65});
private static final String UP_ARROW_2 = new String(new byte[] {27, 91, 65});
private static final String DN_ARROW_1 = new String(new byte[] {91, 66});
private static final String DN_ARROW_2 = new String(new byte[] {27, 91, 66});
// (...)
public static void main(String[] args) throws IOException {
// (...)
// history
else if (commandLine.startsWith(UP_ARROW_1) || commandLine.startsWith(UP_ARROW_2)) {
System.out.println("up arrow");
}
else if (commandLine.startsWith(DN_ARROW_1) || commandLine.startsWith(DN_ARROW_2)) {
System.out.println("down arrow");
}
// (...)
}
}
但这一切只是为了解释或演示,以便回答你的问题 - 我喜欢得到赏金。 ;-)
也许一种方法不是重新发明轮子并使用其他人的工作,例如:像JLine这样的框架。从我所听到的情况来看,它并不完美,但比你在短时间内自己开发的任何事情都要好得多。有人撰写了关于 JLine 的简短介绍blog post。图书馆似乎只是做你需要的。享受!
更新:我对此代码示例进行了JLine 2.11一点尝试(基本上是博客文章中的一个以及标签文件名完成情况:
import java.io.IOException;
import jline.TerminalFactory;
import jline.console.ConsoleReader;
import jline.console.completer.FileNameCompleter;
public class MyJLineShell {
public static void main(String[] args) {
try {
ConsoleReader console = new ConsoleReader();
console.addCompleter(new FileNameCompleter());
console.setPrompt("prompt> ");
String line = null;
while ((line = console.readLine()) != null) {
console.println(line);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
TerminalFactory.get().restore();
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
它在Windows和Linux上运行良好,但对我来说,标签完成仅适用于Linux,而不适用于Windows。无论如何,命令历史记录在两个平台上都运行良好。
答案 1 :(得分:2)
VK_UP是一个整数常量,而in.readLine()是一个字符串。 他们不会彼此平等。为什么不尝试在单击向上箭头时测试控制台中出现的代码?所以喜欢:
if (in.readLine().equals("^[[A"))
然后你可以清除该行,并在具有最高索引的arraylist中写入命令。
另外,我测试了这个,发现了一个bug。将第一句之外的if
语句更改为else if
;在任何命令之后它最终会到达else
并显示“不正确的命令”