There are several problems with your approach:
- The user blackSmith mentioned to himself that the processing of the system console is platform dependent when it comes to processing the cursor and similar topics.
BufferedReader.readLine not a smart choice to use in a looping history in the shell, because you want the shell to immediately respond to the cursor keys, rather than forcing the user to press Return or Enter. Reading entire lines is only required for custom commands. Thus, you need to scan keyboard input for each individual character or key code and decide for yourself if it is, for example, the cursor key (up / down to cycle through the history, left / right to move the cursor on the command line) or delete / backspace for command line editing, etc.- Text strings created by reading control characters through
readLine may depend on the OS, possibly even on the shell and character set (UTF-8, ISO-8859-1, US ASCII, etc.) on the console. - Built-in shell editing functions, such as command history, can interfere with
readLine , for example. on Linux, I see "^ [[A] to move the cursor up, on Windows the cursor keys are passed to the built-in cmd.exe command history function. That is, you need to put the console in raw mode (editing the line is excluded and the Enter key is not required), unlike from the prepared mode (editing a line with the required Enter key).
In any case, to answer your initial question about how to find out which key codes are created using BufferedReader.readLine , it's actually quite simple. Just upload the bytes to the console as follows:
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();
Under the Linux cursor, there might be something like “27, 91, 65” or just “91, 65”, depending on the terminal. the down cursor ends with "66" instead of my system. So you can do something like:
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});
But all this is only for explanation or demonstration and in order to answer your question - I really want to get generosity. ;-)
Perhaps the way is not to reinvent the wheel and use the work of others, for example. a framework like JLine . It is not perfect from what I heard, but goes further than anything that you can develop yourself in a short time. Someone wrote a short introductory blog post about JLine. It seems that the library is doing what you need. Enjoy it!
Update: I gave JLine 2.11 a little try with this sample code (basically the one that was added to the blog post plus adding the tab name:
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(); } } } }
It works fine on Windows and Linux, but for me tab completion only works on Linux, not Windows. In any case, the history of teams works well on both platforms.