Add story to custom shell

I am creating a custom wrapper in Java. I added a story to it so that when I press the up arrow, it goes to the previous command, but the up arrow does not seem to work.

Here is my code:

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++; } } } 

All that I get

 Shell>^[[A Incorrect Command Shell> 

Any thoughts?

+4
source share
2 answers

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}); // (...) 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"); } // (...) } } 

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.

+6
source

VK_UP is an integer constant, and in.readLine () is a string. They will not be equal to each other. Why aren't you trying to check the code that appears on the console, usually when you click on the arrow? So that:

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

and then you can clear the line and write the command to arraylist with the highest index.

In addition, I checked this and found an error. Change your if statements except the first to else if ; after any command, it will eventually go to else and display "Invalid command"

+2
source

Source: https://habr.com/ru/post/1489139/


All Articles