【问题标题】:Add History to Custom Shell将历史记录添加到自定义 Shell
【发布时间】:2013-07-01 18:09:44
【问题描述】:

我正在用 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>

有什么想法吗?

【问题讨论】:

  • 恕我直言,您应该切换到基于摇摆的控制台,在该控制台中可以完全控制键事件,而无需任何平台依赖性。否则可能会找到解决方案,但将是本机的或最终使用 3rd 方 api。好吧,这不是一个答案,而只是一个建议。顺便说一句,我猜你还没有在 Windows 机器(dos)上尝试过你的程序。它的 upArrow 完美地提供了历史。

标签: java shell


【解决方案1】:

您的方法存在几个问题:

  • 用户 blackSmith 在我之前提到,在光标键处理和类似主题方面,系统控制台处理是平台相关的。
  • BufferedReader.readLine 不是用于在 shell 中循环历史的明智选择,因为您希望 shell 立即对光标键做出反应,而不是强制用户按 Return 或 Enter。只有用户命令才需要读取整行。因此,您需要扫描每个单个字符或键码的键盘输入,并自行决定是否是例如光标键(上/下用于历史循环,左/右用于在命令行中移动光标)或删除/退格用于命令行编辑等等。
  • 通过readLine 读取控制字符创建的文本字符串可以依赖于操作系统,甚至可能依赖于外壳和字符集(UTF-8、ISO-8859-1、US ASCII 等)控制台。
  • 命令历史记录等内置 shell 编辑功能可能会妨碍readLine,例如在 Linux 上,我看到“^[[A” 向上光标的东西,在 Windows 上,光标键被传递到 cmd.exe 的内置命令历史记录功能。 IE。您需要将控制台置于 raw 模式(绕过行编辑,无需 Enter 键),而不是 cooked 模式(需要使用 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 上运行良好,但对我而言,tab 补全仅适用于 Linux,不适用于 Windows。无论如何,命令历史在两个平台上都运行良好。

【讨论】:

    【解决方案2】:

    VK_UP 是一个整数常量,而 in.readLine() 是一个字符串。 他们不会互相平等。为什么不尝试测试通常在单击向上箭头时出现在控制台中的代码?比如:

    if (in.readLine().equals("^[[A"))

    然后你可以清除该行,并将命令写入具有最高索引的arraylist中。

    另外,我对此进行了测试并发现了一个错误。将除第一个之外的 if 语句更改为 else if;在任何命令之后它最终会到达else 并显示“不正确的命令”

    【讨论】:

    • 试过 if (console.readLine().equals("^[[A")) { System.out.println("向上箭头"); } 似乎不认识它。
    • 请改用“\u001B[A”。 Escape 呈现为“^[”,但实际上是一个单独的、不可打印的字符。
    猜你喜欢
    • 1970-01-01
    • 2014-07-01
    • 2020-05-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-11-25
    • 1970-01-01
    相关资源
    最近更新 更多