将历史记录添加到Custom Shell

Bad*_*ral 4 java shell

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

        }
    }
}
Run Code Online (Sandbox Code Playgroud)

我得到的只是

Shell>^[[A
Incorrect Command
Shell>
Run Code Online (Sandbox Code Playgroud)

有什么想法吗?

kri*_*aex 6

您的方法有几个问题:

  • 用户blackSmith在我之前已经提到过,在光标键处理和类似主题方面,系统控制台处理依赖于平台.
  • BufferedReader.readLine在shell中使用历史循环不是一个明智的选择,因为你希望shell立即对光标键做出反应而不强迫用户按Return或Enter.只有用户命令才需要读取整行.因此,您需要扫描每个单个字符或键代码的键盘输入,并自行决定是否为光标键(历史循环向上/向下,命令行中光标移动的左/右)或删除/退格用于命令行编辑等.
  • 通过读取控制字符创建的文本字符串readLine可能取决于操作系统,甚至可能取决于控制台上的shell和字符集(UTF-8,ISO-8859-1,US ASCII等).
  • 内置的shell编辑功能(如命令历史记录)可能会妨碍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();
Run Code Online (Sandbox Code Playgroud)

在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");
            }
        // (...)
    }
}
Run Code Online (Sandbox Code Playgroud)

但这一切只是为了解释或演示,以便回答你的问题 - 我喜欢得到赏金. ;-)

也许一种方法不是重新发明轮子并使用其他人的工作,例如像JLine这样的框架.从我所听到的情况来看,它并不完美,但比你在短时间内自己开发的任何事情都要好得多.有人撰写了一篇关于JLine的简短介绍性博客文章.图书馆似乎只是做你需要的.请享用!


更新:我使用此代码示例尝试了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();
            }
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

它在Windows和Linux上运行良好,但对我来说,选项卡完成仅适用于Linux,而不适用于Windows.无论如何,命令历史记录在两个平台上运行良好.