Background
I started a command prompt from a Java application. Now I want to input commands into the command prompt that was just generated. How can you run commands in the prompt that was just generated by the Java program?
What I've tried
My code creates a process that starts the command prompt. And then it gets the process's OutputStream to try and write to it. But I don't see any changes happening. It should just change directories and then run a series of commands in the new directory.
// Block that makes new command prompt
List<String> commands = new ArrayList<String>();
commands.add("cmd.exe");
commands.add("/c");
commands.add("start");
commands.add("cmd.exe");
// Block that creates a writer to write to new command prompt
ProcessBuilder pb = new ProcessBuilder(commands);
Process p = pb.start();
OutputStream os = p.getOutputStream();
BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(os));
// Block that actually writes the commands
writer.write(String.format("cd %s\n", PATH);
writer.write(OTHER_COMMANDS); // I'm ommiting the other commands because there's a lot
writer.flush();
writer.close();
I'm unsure why the commands aren't written to the command prompt that pops up. I see that a new command prompt pops up, but the directory is unchanged from where it starts. How can I input commands into the prompt that I generate?
When you're writing to p, you're writing to this command's standard input:
cmd.exe /c start cmd.exe
You're writing to the first cmd.exe. Which does not do anything except start the second one. It is not possible (at least not simple) to get a handle to the second process. You can use /K, and merge the commands with && after each other. For example:
commands.add("/K");
commands.add(String.join(" && ", OTHER_COMMANDS_AS_LIST));
Your program ProcessBuilder gives you access to the stdin, stdout, and stderr of the process cmd.exe. Those are not the same things as the input and the output of a window and command prompt opened up by the program cmd.exe
There is an answer here regarding how to do it with c#: Create a cmd window and write to it from C# application
In Java I am not aware of a similar API, it might actually require using JNI or JNA in order to gain access to the Windows APIs you would need to use. The function you use (through JNI or JNA) would be the same https://learn.microsoft.com/en-us/windows/console/allocconsole referenced in that other answer. So that is one way to do it, but a full description of how to use either JNI or JNA is outside the scope of an answer here, should you choose to go that route.
As far as I can see, you're trying to implement something vaguely similar to a remote shell (SSH without network and without encryption)...
This should work like you expect for a *nix-like shell, because those shells handle their standard input and output correctly, precisely for the case that they need to be redirected (over a network, or whatever).
The Windows cmd.exe is actually not a pure shell. It is a terminal emulator (the black window that appears on the screen) and the shell, in one process. That's why it doesn't get its input from stdin, and doesn't print its output on stdout. Instead, it listens to GUI keyboard events for input and handles them internally, and the output is displayed directly in the window, without writing it to stdout.
That's why there is no easy way to "remote control" a cmd.exe. It's just not made for that.
You can try this with "proper" shells, like a Cygwin'ed bash.exe, or maybe PowerShell, or similar.
It's actually astonishing that the cmd.exe which was created as a quick'n'dirty "DOS window" some 20 years ago, is still surviving to this day and is actually used for production work...
Related
I have a program which runs when you double click it. What i want to do is:
Double click a jar File
Launch some GUI
And Run Commands in the terminal through the java program
(just an example, this is not what i want to do)
I Tried this:
public class Main{
public static void main(String [] args){
Runtime.getRuntime().exec("/bin/bash sudo SOME_COMMAND_HERE");
}
}
(SOME_COMMAND_HERE is just a replacement)
It didn't launch a terminal.
(i have the GUI part so no need to write code for that)
So how do i do this? I have been spending a past day or two for just finding answers on stack overflow, and intense googling.
Plus the Reason why i need it is because i want to show the user some progress and entering the password when running sudo commands.
Can some Please help? Thanks in advance.
In your example the process might get stuck as you are not reading it's stdout and stderr streams. From the Process documentation:
Because some native platforms only provide limited buffer size for standard input and output streams, failure to promptly write the input stream or read the output stream of the process may cause the process to block, or even deadlock.
So essentially you are just asking how to execute a process from Java. It is irrelevant that to the user your program would display a terminal-like UI. Knowing this we could rephrase your question and find answers like
Starting a process in Java?
Java Execute a bash script using Java process builder
Execute process from Java irrespective of underlying OS
Java ProcessBuilder - get Output immediately
https://alvinalexander.com/java/java-exec-processbuilder-process-1/
I have a long-running Java server application that launches a sub-process to perform a particular task (in this case, extract the contents of a 7zip file using the 7z command-line utility, but that particular detail shouldn't be relevant here).
The server application runs with Java 8 under Ubuntu 14.
The sub-process is being launched via the Java ProcessBuilder API.
The file being accessed by the sub-process might be password-protected.
If the file is password-protected and no password is supplied as a command-line argument, the 7z program will attempt to display a message to the terminal prompting for a password, and then read the password from the terminal.
At this point, the sub-process hangs and will not complete unless I hit <Enter> twice in the terminal that controls the Java process.
The underlying issue seems to be that the 7z utility uses the getpass system call to display the prompt and read user input. According to the docs:
The getpass() function opens /dev/tty (the controlling terminal of the process), outputs the string prompt, turns off echoing, reads one line (the "password"), restores the terminal state and closes /dev/tty again.
Since this is a server application, it is not acceptable to have the sub-process block waiting on input from the Java terminal. What I would like to happen instead is to programmatically shut down input on the sub-process terminal, so that the getpass call returns immediately with no input and the sub-process exits with an error code. Unfortunately all of my attempts at shutting down input to the sub-process terminal have resulted in the same behavior as above:
I tried manually closing the stream returned by Process.getOutputStream() immediately after starting the sub-process.
I tried using ProcessBuilder.redirectInput to redirect sub-process input to read from an empty file, and from /dev/null.
For good measure, I even tried passing Redirect.INHERIT to ProcessBuilder even though that doesn't seem like it's remotely what I want.
It seems like this should be feasible, because I've observed the desired behavior when 7z is launched with the exact same command-line as a sub-process of a daemonized socat process where the terminal input is connected to /dev/null, but I'm at a loss for how to accomplish this in Java using the tools at my disposal.
In order to preclude that the getpass() function opens /dev/tty (the controlling terminal of the process), we have to arrange that the utility has no controlling tty, which is achieved by calling setsid(). This C program (let's name it notty) makes this call and then executes the given utility:
int main(int argc, char *argv[])
{
setsid(); // get rid of the controlling tty
close(0); // preclude reading STDIN as well
execvp(argv[1], argv+1); // execute the given program file
}
We can use it from the Java application by prepending it to the ProcessBuilder command, e. g.:
ProcessBuilder pb = new ProcessBuilder("notty", "7z", … /*arguments*/);
I normally use java.lang.ProcessBuilder and java.lang.Process to run external command line programs, and it works fine for run-and-done commands. For example, this would run "myProgram" with argument "myArg" in the working directory:
List<String> commandLine = new ArrayList<String>();
commandLine.add("myProgram");
commandLine.add("myArg");
ProcessBuilder builder = new ProcessBuilder(commandLine);
builder.redirectErrorStream(true);
Process process = builder.start();
However, say I wanted to run a script or program or something that had interactive input (it prompted me for more input after starting). Can I do that in Java with code similar to that above, or do I need a different approach? Or is there some library that can help me with this?
According to the documentation you should be able to redirect the input and output streams. This tells it to use the System.in/System.out from the parent process:
builder.redirectInput(ProcessBuilder.Redirect.INHERIT);
builder.redirectOutput(ProcessBuilder.Redirect.INHERIT);
If you want to write things to the processes's input:
If the source is Redirect.PIPE (the initial value), then the standard input of a subprocess can be written to using the output stream returned by Process.getOutputStream(). If the source is set to any other value, then Process.getOutputStream() will return a null output stream.
Redirecting stdin and stdout is certainly one option for simple command-line programs.
Using a "robot" class is another, if you actually need to script keystrokes (for example, in a test script):
http://www.java-tips.org/java-se-tips/java.awt/how-to-use-robot-class-in-java.html
http://download.java.net/jdk7/archive/b123/docs/api/java/awt/Robot.html
Writing a simple .bat file or shell script, that calls your Java program and uses "<" and ">" redirection operators is yet a third option.
It all depends on exactly what you're looking for :)
I have some simple code that uses Java apache exec classes to run external processes.
Executor ex = new DefaultExecutor();
ex.setStreamHandler(new PumpStreamHandler(System.out, System.out, System.in));
CommandLine cl = new CommandLine(
"C:\\program.exe");
ex.execute(cl);
}
For certain command line programs, this works as expected and gets all the program's output into the "out" stream while accepting my own text into the "in" stream. However, for other programs, the output of the process is visible running manually from command line, but I don't get anything coming in when I run via java process.
I would like to eventually write to the stdin and retrieve and analyze stdout within the code itself.
If there a reason that I don't know of, why some programs seem to output text on the command line, yet when I run them as java processes, I don't receive anything through the streams?
This is happening in Windows.
Out of process code will not go to the same command line output unless you explicitly configure it to do so. Also, as a general rule it is better to use a logging library like log4j than to do println statements.
I am wanting to run a .jar file and display the output (I'm assuming I would use a rich text box) however I also want to have a text box that will allow me to send commands to the running jar file as if it were running in a command prompt window.
Either this or embedding the command prompt would be ideal. I have messed around with code and have never got it to work.
I would also like to detect certain messages that the jar file returns, for example if a certain command is ran it will display a message depending on whether or not that failed, is there a way to read everything that get returned and then have an event happen if it detects this certain string?
How To: Execute command line in C#, get STD OUT results I have been looking at things such as that, when I say I have messed around with code, I have tried the code given in these examples (changing small parts) they never worked.
To summarise:
- I want to run a jar file that obviously displays it's output in a command prompt.
- I want to hide the command prompt and display it's output in a rich text box.
- The jar file will return string such as 'user 1 has logged in' I want to update a labels text everytime that happens.
You should have a look at the ProcessBuilder and Process classes. They allow to run command line programs and read/write in the process streams.
Also, if your jar is known in advance, why no simply using the class as if it would be in a library and use the exposed APIs?
When we started learning java at school we used
String myInput = JOptionPane.showInputDialog(this, "myPrompt");
and
JOptionPane.showMessageDialog(null, "A basic JOptionPane message dialog");
for input and output. Very simple.
Or you can use the command-line:
BufferedReader br = new BufferedReader(new InputStreamReader(System.in)
String input = br.readLine();