2013-07-01 119 views
4

我正在使用Java创建自定义外壳程序。我已经加入历史上它,这样当按下向上箭头它转到前面的命令,但向上箭头似乎没有工作将历史记录添加到自定义外壳程序

这里是我的代码:

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

恕我直言,你应该切换到一个基于Swing的控制台,在的KeyEvents完全控制是可能的,没有任何platf orm依赖。否则,可能会找到一个解决方案,但将是本地的或结束使用第三方API。那么,这不是一个答案,而只是一个建议。顺便说一句,我猜你还没有尝试过你的程序在Windows机器上(DOS)。这是最好的提供完美的历史。 – blackSmith

回答

6

有几个问题你的方法:

  • 用户铁匠我之前已经提到,系统控制台处理是与平台相关的当涉及到光标键处理和类似的主题。
  • BufferedReader.readLine在shell中用于历史循环不是一个明智的选择,因为您希望shell立即对光标键作出反应并且不会强制用户按Return或Enter。只有用户命令才需要读取整行。因此,你需要扫描每个单个字符或键码的键盘输入,并自己决定它是否是例如光标键(历史循环上/下,命令行内光标移动的左/右)或用于命令行编辑的删除/退格等等。
  • 通过读取控制字符readLine创建的文本字符串可能依赖于操作系统,甚至可能依赖于控制台上的shell和字符集(UTF-8,ISO-8859-1,US ASCII等)。
  • 内置shell编辑功能(如命令历史记录)可能会妨碍readLine,在Linux上,我看到“^ [[A]光标向上的东西,在Windows上光标键被传递到cmd.exe的内置命令历史记录功能。即您需要将控制台放在原始模式下(线路编辑绕过并且不需要Enter键),而不是烹饪模式(需要使用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和显示“不正确的命令”

+0

尝试了如果(到Console.ReadLine()等于( “^ [[A”)。) { 的System.out.println( “向上箭头”); } 似乎无法识别它。 – Badmiral

+0

尽管使用“\ u001B [A”。 Escape被渲染为“^ [”,但实际上是一个单独的,不可打印的字符。 –

相关问题