Windows Cmd Prompt streams
The ultimate goal of the project I'm currently working on is to create a Swing command prompt which uses Window's Cmd prompt to execute commands. I have the swing portion finished, now I just need to stream input/output between my Java program and the command prompt.
Now I want the underlying functionality of this command prompt to be fairly similar to what the real thing will do (granted with a few exceptions, these are kind of irrelevant in this topic), which involves supporting stuff like batch files and the set command.
It isn't a problem to get one single command to work (or even running a bunch of commands in a batch file), but I'm having problems getting multiple user-entered commands to work.
For example:
Code :
SET IS_OK=1
IF IS_OK EQU 1 (ECHO "It is ok.")
ECHO "DONE"
The expected output if a user were to type this into a real command prompt would be to print out "It is ok.", followed by "DONE" (without quotes)
The way I currently have it setup is to run a new command prompt for every command. This automatically dis-allows the above from working because set only works in an individual's environment variable.
Here's a short example of what I'm doing:
Code Java:
import java.io.IOException;
import java.util.Scanner;
public class Test
{
public static void main(String[] args) throws IOException, InterruptedException
{
runWndCommand("SET IS_OK=1");
runWndCommand("IF IS_OK EQU 1 (ECHO It is ok.)");
runWndCommand("ECHO DONE");
}
/**
* runs a Windows command prompt with the given command. Output is printed
* out to System.out. Not entirely robust, but it demonstrates the point.
*
* @param cmd
* @throws IOException
* @throws InterruptedException
*/
public static void runWndCommand(String cmd) throws IOException, InterruptedException
{
Runtime runtime = Runtime.getRuntime();
Process p = runtime.exec(new String[] { "cmd.exe", "/C", cmd });
Scanner reader = new Scanner(p.getInputStream());
while (reader.hasNext())
{
System.out.println(reader.nextLine());
}
p.waitFor();
}
}
The output of this code is to only print out "DONE"
Before you start saying that I could pass multiple commands in the runtime.exec() call, remember that this is for a command prompt. As such, individual calls are very likely to be executed before the next command would even be entered.
Is something like this even possible to do (without resorting to JNI, JVM mods, etc.)?
I've tried piping input to the command process using p.getOutputStream() (and running the command prompt without the /C flag), but the command prompt doesn't seem to respond to any inputs I put through this stream.
Oh, and I would really prefer not to re-write the windows command prompt functionality all in Java.
cross-posted at Cmd process streams - Java Forums.
Re: Windows Cmd Prompt streams
Have you tried keeping the Process open before running the next command? For example retrieve the Input and Output streams of the Process, pipe those commands to the OutputSteam, and do both input/output in their own threads? I'm not sure this will work, but its worth a quick try.
Re: Windows Cmd Prompt streams
Yes, I've tried getting the input/output streams, but it's not processing any inputs I'm sending. I think the problem is because I can't think of any good way to send a "return" signal to indicate that a complete statement has been entered. I can send the '\n' character (or \r\n sequence), as well as the -1 byte value (which is used in some Java streams to denote the end of a read sequence), but none of these have the desired effect.
Code Java:
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
public class Test
{
public static void main(String[] args) throws IOException, InterruptedException
{
cmd = Runtime.getRuntime().exec("cmd.exe");
cmdOutput = cmd.getInputStream();
cmdInput = cmd.getOutputStream();
Thread display = new Thread(new StreamGobbler(cmdOutput, ""));
display.setDaemon(true);
display.start();
runWndCommand("SET IS_OK=1");
runWndCommand("IF IS_OK EQU 1 (ECHO It is ok.)");
runWndCommand("ECHO DONE");
runWndCommand("EXIT");
cmd.waitFor();
}
private static Process cmd;
private static InputStream cmdOutput;
private static OutputStream cmdInput;
/**
* runs a Windows command prompt with the given command. Output is printed
* out to System.out
*
* @param cmd
* @throws IOException
* @throws InterruptedException
*/
public static void runWndCommand(String cmd) throws IOException, InterruptedException
{
System.out.println("input> " + cmd);
for (int i = 0; i < cmd.length(); ++i)
{
cmdInput.write(cmd.charAt(i));
}
cmdInput.write('\r');
cmdInput.write('\n');
cmdInput.write(new byte[] { -1 });
}
}
Here's the stream gobbler class. It basically just prints out all inputs it can read from the stream to System.out, but does so on a separate thread.
edit:
I was wondering, what about using a Java robot? I can only seem to be able to attach the robot to a particular GraphicsDevice, but Runtime.exec() doesn't provide a cmd prompt terminal window (also, such a window is not desired). Is it possible to send the key event to a particular process even though it doesn't have a windows handle (and is focused)?
Re: Windows Cmd Prompt streams
Finally solved my problem! I forgot to flush the cmdInput stream (writing the newline character is also important) :P Just in case anyone wants, here's the code that works:
Code Java:
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.PrintStream;
public class Test
{
public static void main(String[] args) throws IOException, InterruptedException
{
cmd = Runtime.getRuntime().exec("cmd");
cmdOutput = new BufferedInputStream(cmd.getInputStream());
cmdInput = new PrintStream(new BufferedOutputStream(cmd.getOutputStream()));
Thread display = new Thread(new StreamGobbler(cmdOutput, ""));
display.setDaemon(true);
display.start();
runWndCommand("set IS_OK=\"1\"");
runWndCommand("echo %IS_OK%");
runWndCommand("EXIT");
cmd.waitFor();
}
private static Process cmd;
private static InputStream cmdOutput;
private static PrintStream cmdInput;
/**
* runs a Windows command prompt with the given command. Output is printed
* out to System.out
*
* @param cmd
* @throws IOException
* @throws InterruptedException
*/
public static void runWndCommand(String cmd) throws IOException, InterruptedException
{
cmdInput.println(cmd);
cmdInput.flush();
}
}