run interactive command line application from java - java

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 :)

Related

How do you write to a command prompt in Java?

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...

Access sub-process controlling terminal from Java process

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*/);

Run a python or other language code inside Java

We are building a grading system, and part of its job is to take input files, and from the given directory, our system will compile and run a (non-java) source code file written by the students. Then, the system will display the output from that run.
It is not limited to python, any other language that can meet the requirement is OK.
Is there is any method for providing for the location of the python code and the input file, and then run that code, returning the output file. If not, how can i achieve this goal?
Take a look at the Runtime class. In particular, look at Runtime.exec(). It should enable you to execute external applications from within your Java program, as well as passing command line arguments, and specifying working directory.
Note that the python or any other program must have some well-defined way to get its input and write its output, such as passing filenames on the command line, or reading from stdin.

Missing command line output from Java process in Windows

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.

Detecting input redirection file

How can we detect/fetch the name of a file that's fed as an input redirection to a java program from the command line?
Without OS-specific hacks (such as running ps -ef) you can't.
Why do you need this information, and why can't you restructure your program to simply take the file as a command-line argument?

Categories

Resources