I'm trying to use Java to interface with a large batch file that uses psexec to execute commands on remote servers.
I'm able to launch the file using process builder and it works fine for most commands, but seems to be getting hung up.
One particular command from the batch file is as follows:
ECHO .
Echo Which would you like to reboot?
Echo 1-10. For computers, enter computer number.
Echo E. Exit
set /p userinp=choose a number(0-22):
but from Java I get:
.
Which would you like to reboot?
1-10. For computers, enter computer number.
E. Exit
and then it hangs
It's clearly not reading the set line, but more importantly I haven't yet figured out how to pass input back to the subprocess.
String[] command = {"cmd", "/c", "batchfile", "restart"};
ProcessBuilder builder = new ProcessBuilder(command);
builder.directory(new File("C:\\"));
Process process = builder.start();
InputStream is = process.getInputStream();
InputStreamReader isr = new InputStreamReader(is);
BufferedReader br = new BufferedReader(isr);
String line;
while ((line = br.readLine()) != null) {
System.out.println(line);
}
Any input would be appreciated.
Your batch job requires that you actually provide input in order to proceed, which is why it appears to 'hang'. You need to supply this input to the process, via its output stream. A highly simplified example:
PrintWriter writer = new PrintWriter(process.getOutputStream());
writer.println("10");
writer.flush();
Your process doesn't hang, it is just waiting for some input at the command line, before to proceed.
As you are reading the output from the process via Process.getInputStream(), you can send input back to it using Process.getOutputStream().
public abstract OutputStream getOutputStream()
Gets the output stream of the subprocess. Output to the stream is piped into the standard input stream of the process represented by this Process object.
Implementation note: It is a good idea for the output stream to be buffered.
Returns:
the output stream connected to the normal input of the subprocess.
Related
This question already has answers here:
Java Runtime.getRuntime(): getting output from executing a command line program
(12 answers)
Closed 2 years ago.
I have a standard Maven project and I want to run the meTypeset script. This script takes 3 args where the second one is a file and the third one is a folder created as output.
This is how the script runs in a cmd:
meTypeset.py docx <input> <output_folder> [options]
This is how I try to run it in Java:
public static void main(String args[]) {
String[] cmd = {
"python",
"resources\\pyscripts\\meTypeset.py",
"docx",
"resources\\exampledocs\\example_journal.docx",
"resources\\output"
};
try {
Runtime.getRuntime().exec(cmd);
} catch (IOException e) {
e.printStackTrace();
}
}
Nothing happens, no errors but no result also
Unlike python Java may need some help. Do I guess correctly you are running on Windows?
You invoke the Runtime.exec() method. The method returns a Process instance, and in it's documentation you can read
By default, the created process does not have its own terminal or
console. All its standard I/O (i.e. stdin, stdout, stderr) operations
will be redirected to the parent process, where they can be accessed
via the streams obtained using the methods getOutputStream(),
getInputStream(), and getErrorStream(). The parent process uses these
streams to feed input to and get output from the process. 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 it is likely your process is started by the OS but gets blocked due to I/O restrictions. Get around that by reading the STDOUT and STDERR streams until your process finishes. One good programming model is visible at https://www.baeldung.com/run-shell-command-in-java
#Hiran Chaudhuri explained the error correctly. I am just posting how I solved it, thanks to # Sonnenhut comment.
Runtime rt = Runtime.getRuntime();
String[] commands = {
"python",
"src\\main\\resources/pyscripts/meTypeset.py",
"docx",
"src\\main\\resources/exampledocs/example_journal.docx",
"src\\main\\resources/output"
};
Process proc = rt.exec(commands);
BufferedReader stdInput = new BufferedReader(new
InputStreamReader(proc.getInputStream()));
BufferedReader stdError = new BufferedReader(new
InputStreamReader(proc.getErrorStream()));
// Read the output from the command
System.out.println("Here is the standard output of the command:\n");
String s = null;
while ((s = stdInput.readLine()) != null) {
System.out.println(s);
}
// Read any errors from the attempted command
System.out.println("Here is the standard error of the command (if any):\n");
while ((s = stdError.readLine()) != null) {
System.out.println(s);
}
}
I've stored my python code in file and then input is passed through input.txt.
String rollno="13F127";
String file="add";
Process p = Runtime.getRuntime().exec("C:\\Python34\\python C:\\Users\\Raga\\Documents\\"+rollno+"\\"+file+".py < C:\\Users\\Raga\\Documents\\"+rollno+"\\input.txt");
When I run it using jsp file, it takes long time to load and output didnt come. Please help me with this.
I've read this process output using buffered and input reader.
stdInput = new BufferedReader(new InputStreamReader(p.getInputStream()));
stdError = new BufferedReader(new InputStreamReader(p.getErrorStream()));
Please help me with this!
Do not use redirections (<, >, or |) with exec!
The redirections are read and translated by the interactive shells (here cmd.exe) where they read a command from their standard input. The shell then opens the relevant files and calls the program with redirected standard streams.
exec just does this last part, and passes the < ...input.text as two arguments to the Python program... that do not processes them and also passes them to the script that do not process them either! So the child tries to read on standard input, and keeps waiting here.
So you should:
use ProcessBuilder which according to Runtime javadoc is now the preferred way to start a process with a modified environment
redirect input stream for the subprocess to the file
More or less:
ProcessBuilder pb = new ProcessBuilder("C:\\Python34\\python",
"C:\\Users\\Raga\\Documents\\"+rollno+"\\"+file+".py");
pb.redirectInput(Redirect.fromFile("C:\\Users\\Raga\\Documents\\"+rollno+"\\input.txt"));
Process p = pb.start();
How do I pipe a file into the stdin of a process using ProcessBuilder? The program I am reading the stream from is written in C, but I do not know anything else about it.
Here is the relevant java:
ProcessBuilder pb = new ProcessBuilder("./program").inheritIO();
Process p = pb.start();
DataInputStream din = new DataInputStream(new FileInputStream("./my-file.txt"));
byte[] dinBytes = new byte[din.available()];
din.readFully(dinBytes);
din.close();
OutputStream os = p.getOutputStream();
os.write(dinBytes);
os.close();
int rc = p.waitFor();
System.out.println("RC: " + rc);
Which returns the following error:
java.io.IOException: Stream closed
at java.lang.ProcessBuilder$NullOutputStream.write(ProcessBuilder.java:434)
at java.io.OutputStream.write(OutputStream.java:116)
at java.io.OutputStream.write(OutputStream.java:75)
at Test.main(Test.java:14)
$ input in flex scanner failed
The cause for your error is the call to .inheritIO() when you build the process. This causes all the process's standard file descriptors to be inherited from the Java process. The documentation says:
This is a convenience method. An invocation of the form
pb.inheritIO()
behaves in exactly the same way as the invocation
pb.redirectInput(Redirect.INHERIT)
.redirectOutput(Redirect.INHERIT)
.redirectError(Redirect.INHERIT)
And the documentation for redirectInput(Redirect) says:
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.
So, basically, the default is that there is a pipeline between the Java process and the built process, so that you can push in data from your program to the process. But you released the standard input of the process from the pipe and let it wait for input from the user console, but then you tried to treat it as if it's still part of the pipe, and get your end of the pipe in order to push data inside. You get a null output stream, and as soon as you try to put something in it, you get an exception.
So you shouldn't make that call. And since you want input from a text file, the easiest thing to do would actually be to redirect that input stream to the file directly. Again, redirecting breaks the pipe - but you won't need it because the redirection actually does what you need:
// Take input from a file, output and error go to user console
pb = new ProcessBuilder("./program")
.redirectInput(new File("./my-file.txt"))
.redirectOutput(Redirect.INHERIT)
.redirectError(Redirect.INHERIT);
Process p = pb.start();
int rc = p.waitFor();
An alternative method, in case you want to pipe in more than one file, is to keep the pipe to the process standard input around, but copy the files using Files:
pb = new ProcessBuilder("./program")
.redirectOutput(Redirect.INHERIT)
.redirectError(Redirect.INHERIT);
Process p = pb.start();
OutputStream os = p.getOutputStream();
Files.copy( Paths.get("./my-file-1.txt"), os );
Files.copy( Paths.get("./my-file-2.txt"), os );
// more files
os.close();
int rc = p.waitFor();
Regarding the DataInputStream: it is a type of InputStream used for processing binary data that was written from a DataOutputStream - with ints, doubles etc. written in binary form. Of course, like any InputStream, you can read bytes from it as-is. But for that, you don't really need to wrap it in a DataInputStream.
I have a myfile.jar file which I'm executing through another Java program using ProcessBuilder and trying to read the console output of the jar using InputStream as follows:
ProcessBuilder pb = new ProcessBuilder("java", "-jar", "myfile.jar", "arg1");
Process p = pb.start();
InputStream in = p.getInputStream();
InputStreamReader ins = new InputStreamReader(in);
BufferedReader br = new BufferedReader(ins);
String line;
while ((line = br.readLine()) != null) {
System.out.println(line);
}
p.waitFor();
p.destroy();
When I execute the above code, I'm not getting anything printed on the console. The myfile.jar was not developed by myself. But when I read through the code of myfile.jar it uses the Logger to print on the console using ConsoleHandler. I assume this may be the issue.
EDIT: When java -jar myfile.jar arg1 is executed standalone on the console, it prints the output on the console.
This is driving me nuts for past 3 days. The direction to solve this problem would help me to learn more.
When processing an external process, you should not only consume/monitor the stdout, but also the stderr.
You could use a second reader to read the stderr stream from the process, but generally it's easier to combine the streams using ProcessBuilder#redirectErrorStream, which redirect the stderr through the processes InputStream.
Sometimes a process will output to the stderr instead of the stdout.
Even if you're not using the output of these streams, it's always a good idea to read them as some processes can stall if the streams are not flushed.
I want to run an executable written in C++ and to see the cmd promt associated with it in foreground, since the executable prints some lines in the cmd.
I have written the following code, but all processes are created and run in background (In this code I open the dummy cmd.exe process, not my process).
Process p = new ProcessBuilder("C:\\Windows\\System32\\cmd.exe").start();
How can i enable foreground running of processes?
Thanks!
The issue is not whether the process is in the foreground or background. When you start a process using Java, you have to use Java to control that process' lifecyle. The Java API provides you access to various attributes of the process. What you're interested in here is the output of the process. That is represented by the process' InputStream. It seems counterintuitive, but it makes sense because from the perspective of your Java program, the process' output is the program's input. Conversely, if you need to send data to the process, you write to the process' OutputStream.
To sum up, access the process' InputStream and print that out to the command-line:
Process process = new ProcessBuilder("C:\\Path\\To\\My\\Application.exe").start();
BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()));
StringBuilder output = new StringBuilder();
String line;
while ((line = reader.readLine()) != null) {
output.append(line);
}
System.out.println(line);
This code, of course, assumes that your process is not waiting for any input, i.e., it is not interactive.
Vivin Paliath's answer is really the way to go, then you can do whatever you want with the output, display it in your own dialogue, log it, interpret it, check for errors or whatever.
But just in case you really want that command window showing up. Execute cmd.exe and get the process' OutputStream and write the command (application.exe) to it ending with a new line.
Something along the lines of:
Process p = new ProcessBuilder("C:\\Windows\\System32\\cmd.exe").start();
out = p.getOutputStream();
out.write("path\\application.exe\r\n".getBytes());
out.flush();
Should usually drain the input stream too though anyway.