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.
Related
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...
In a Clojure program, how do you read from standard out? I want to do that, or pipe the standard output, to an input stream that I create. The standard output in Clojure is a java.io.PrintWriter .
I have a Samza job, started by a Clojure program. There's also an nrepl server to which I can remotely connect. After connecting, I need to be able to tap into and tail standard out (to which jobs write their output).
1) As per this SO question, with-out-str (see here) lets us temporarily bind *out* (to a java.io.StringWriter), so that your executed code writes to a string. But that doesn't let me tap into the existing *out*.
2) If you look at clojure.java.shell (see here), it gets the JVM's Runtime and exec's a Process on it. From that process, you can get its standard output stream. But again, that's not the default standard out (*out*) I'm looking for.
3) This SO question gets close to what I'm trying to do. But again, I'm connecting to an existing process, and want to tail out its standard output.
Is this possible in Clojure (see here)? Has anyone solved this?
Process output is not a publish subscribe model, so in effect when a process puts a character into it's output buffer, exactly one process gets to pull it off that buffer. If you have a program that was started by a shell that shell process if reading it's output and writing it to a terminal (or reading and ignoring it). If you attach your process after the process that started it and start trying to grab the data, you will most likely not get anything because the parent process will get it first. I just tried this from two terminals:
Terminal 1:
cat
Terminal 2:
ps -ef | grep cat
tail -f /proc/24547/fd/2
Terminal 1:
hello
Terminal 2:
< nothing >
The string "hello" printed to terminal 1, the process that started it.
It's tempting then to say "well what if nobody reads the output, then it will be there for me to get". While this sounds good it runs into the problem that these are fixed sized buffers, so as soon as the output buffer is full the process that is trying to write to it blocks (is prevented from running at all) until someone reads the output to unblock it.
The general solution is to pipe the process you want to tail later to the tee command which writes the output to a file and passes it to whatever was reading it.
command-to-watch arg1 arg2 | tee logfile.potentially-huge
Though if you go this route you should rotate the log file before your disk fills. Make sure you empty the log file with exactly this command
echo > logfile.potentially-huge
or use your program to make a truncate call to the file. simply deleting the file will remove it's name from the log directory without deleting it, it will silently continue to grow taking up disk space and the new file will get no output ever.
This is basically why we built log libraries like log4j (in the 90s) and syslog (in the 80s).
If you still want to get hackish crazy on this, turn to tmux, it can do anything, and changes the way people work with text. In all seriousness you should change the way the other process creates it's output to make it easier to get.
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 a batch file which performs the operation of listening to the microphone and converting it to text (i am using pocket sphinx).
The command I am using to run the batch file is pocketsphinx_continuous.exe -dict <dict name> -lm <language model> -hmm <acoustic model location>. The batch file starts off and keeps listening to the microphone. Whenever we finish speaking a sentence it converts it into text on the command prompt. Since this continuously running we terminate this task by Ctrl-C.
I was trying to make this into a standalone Java application. I wanted to run this batch file through Java, so i used Runtime.getRuntime().exec("cmd /c start pocketsphinx_continuous.exe ...") with all the parameters. However strangely, it starts the batch process in a separate command prompt but immediately exits. I tried to use process.waitfor(), however it simply starts executing the batch process and then terminates. I havent called process.destroy, so i am unable to figure out why it exits the batch process.
The other question is since the batch file is running continuously, after every spoken sentence has been transcribed , I wish to get the output in my Java application. I know i can redirect the batch process to a file and then read the file, was just wondering if there is a more direct process. Could you please help me figure out where I am making a mistake.
You should use Process.getInputStream() and Process.getErrorStream() to find out what messages it prints out.
If it is exiting instantly, you might need to get the exit code (Process.waitFor()) and check the error logs from the error stream.
Also, if you can post some of your code we might be able to help. In general, these problems are due to incorrectly configured paths or command strings.
A possible fix would be to use the ProcessBuilder from Java 1.5 thusly:
// Note the lack of "cmd \c" - you shouldn't need to run it in a cmd window!
ProcessBuilder pb = new ProcessBuilder("pocketsphinx_continuous.exe",
"dict", "1121.dic",
"-lm", "1121.lm",
"-hmm", "hub4/hmm");
Process p = pb.start();
// TODO for you:
// 1. Create Threads to handle the input
// 2. Store the Process instance so that you can call p.destroy() later.
// 3. If interested, have a Thread doing p.waitFor() to get the exit code.
As Joachim Sauer mentioned in the comments, this Javaworld article explains a lot of the gotchas associated with the Process API, so have a read through. I haven't had a chance to play with the improvements made in JDK7 to the Process API, and by the looks of things Oracle are improving it again for JDK8 (JEP 102).
You can also use Timer and TimerTask to schedule your batch scan process in background. You can use this to specify infinite running task.
Here is my problem:
I have a .bat file which has a number of commands. When I invoke this bat file from my java program using processbuilder it starts executing the commands in .bat file line by line. However when it reaches the last line which executes a perl program it hangs and the program never ends. It hangs there forever. Any idea why this might be happening. Just for your information there is no problem with the perl code and I'm able to execute
Does your perl program write to standard output?
I suspect the problem is that a Java process redirects the child process' standard output and standard error output to a pipe.
The receiving end of that pipe is connected to the java program, so any child process invoked from java that writes to standard output / error will block if you fail to read its output.
You must either eliminate the perl program's output or read the child process' output (Process.getInputStream() and/or Process.getErrorStream()) from within your java program.
Hey I was able to fix this problem. All I had to do was to include a statement in my java code which closes the output stream of the process. Without which it was waiting on the command line for the perl program to be executed forever. I'm not exactly sure about the behavior, any information would be help