Trouble calling batch file from Java program - java

Everything I read says that the only way to call a batch file from within a java program is to do something like this:
Process p = Runtime.getRuntime().exec("cmd /c start batch.bat");
From what I understand this creates a process to run CMD.exe, which in turn creates a process to run the batch file. However, the CMD.exe process appears to exit once it has instantiated the batch file process.
How can I confirm that the batch file has completed before the CMD process exits?

What jeb said, or try passing the /wait parameter to start. That should cause start to wait till the batch process completes. Try it at the command line first -- faster than rebuilding your Java app.

You could try to start the batch without the "start" command after cmd /c, as the "start.exe" creates a new process for the batch.bat.
Process p = Runtime.getRuntime().exec("cmd /c batch.bat");
This should be the correct form for you.

Process p = Runtime.getRuntime().exec("cmd /c start batch.bat");
To wait for the batch file to finish (exit) you remove the "start" after the /c.
The /c tells cmd.exe to return when cmd is finished, this you want, however, "start" means start a new cmd.exe process to execute the batch file. At this point the cmd.exe you started is finished and exits. Probably not what you want.
Either of the two code snippets below will get the batch file running and get you a Process object that you will need.
Process p = Runtime.getRuntime().exec("cmd /c batch.bat");
Or
ProcessBuilder pb= new ProcessBuilder ("cmd", "/c", "batch.bat");
Process p = pb.start ();
Now that you have a Process object you have multiple ways to wait for your batch file to finish.
The simplest way is .waitFor()
int batchExitCode = -1;
try {
batchExitCode = p.waitFor ();
} catch (InterruptedException e) {
// kill batch and re-throw the interrupt
p.destroy (); // could also use p.destroyForcibly ()
Thread.currentThread ().interrupt ();
}
You could also use waitFor(long timeout, TimeUnit unit) or p.isAlive() if they meet your needs better.
Warning If your batch is going to output a lot of data (probably more than 1024 characters, maybe less) to stdout and / or stderr your program needs to handle this data in some manner, otherwise, the pipe (s) between your program and the batch process will fill up and the batch file will hang waiting for room in the pipe to add new characters and the batch file will never return.
This is the problem that originally brought me to Stack Overflow this time. I had 17,000+ commands in a batch file and each command generated 300+ characters to stdout and any errors executing a command generated 1000+ characters.
Some of the ways to solve this:
#echo off as the 1st line in the batch file, with this the cmd process will not echo each command in the batch file. If your batch file does not generate any other output to stdout or stderr you are done.
If one or more of the commands in the batch file can or may generate a lot of stdout and / or stderr output then you have to handle that data yourself.
If you are using "Runtime.getRuntime().exec" you only have one way to handle this and that is to get the InputStreams for the batch file's stdout and stderr by calling:
InputStream outIS = p.getInputStream (); // this gets the batch file's stdout
InputStream errIS = p.getErrorStream (); // this gets the batch file's stderr
Now you have to read each of the InputStreams (only when they have data or you will wait until you do). Assuming that you get a lot more data from one of the InputStreams than the other you are almost assured of hanging the batch file. There are ways to read multiple streams without hanging, however, they are beyond the scope of my already excessively long answer.
If you are using ProcessBuilder pb= new ProcessBuilder ("cmd", "/c", "batch.bat"); (and this is a major reason to do so) you have a lot more options available to you since you can ask for stdout and stderr to be combined pb.redirectErrorStream (true);, easier to handle one InputStream without blocking, or you can redirect stdout and stderr to files (null files may work on Windows definitely on Unix) so you program doesn't have to handle them.
Don't forget that if your batch file reads data from stdin, you have to handle that as well. This is supported in Process and with more options in ProcessBuilder.

From the output of cmd /?
/C Carries out the command specified by string and then terminates
/K Carries out the command specified by string but remains
Thus what you need is:
Process p = Runtime.getRuntime().exec("cmd /k start batch.bat");

If you want to simply find out whether the batch file process finished or not, why not add...
echo DONE
at the end of the batch file?
Or if you program is for public use, something like...
echo finished>log.txt
would work. Then verify that it is finished from within your java program...
if (new BufferedReader(new FileReader("log.txt")).readLine().equals("finished"))
{
System.out.println("the batch process has finished");
}

Related

JavaFX + ProcessBuilder / Runtime.exec() starts process after JVM closes [duplicate]

I've been trying to use Java's ProcessBuilder to launch an application in Linux that should run "long-term". The way this program runs is to launch a command (in this case, I am launching a media playback application), allow it to run, and check to ensure that it hasn't crashed. For instance, check to see if the PID is still active, and then relaunch the process, if it has died.
The problem I'm getting right now is that the PID remains alive in the system, but the GUI for the application hangs. I tried shifting the ProcessBuilder(cmd).start() into a separate thread, but that doesn't seem to be solving anything, as I hoped it would have.
Basically the result is that, to the user, the program APPEARS to have crashed, but killing the Java process that drives the ProcessBuilder.start() Process actually allows the created Process to resume its normal behavior. This means that something in the Java application is interfering with the spawned Process, but I have absolutely no idea what, at this point. (Hence why I tried separating it into another thread, which didn't seem to resolve anything)
If anyone has any input/thoughts, please let me know, as I can't for the life of me think of how to solve this problem.
Edit: I have no concern over the I/O stream created from the Process, and have thus taken no steps to deal with that--could this cause a hang in the Process itself?
If the process writes to stderr or stdout, and you're not reading it - it will just "hang" , blocking when writing to stdout/err. Either redirect stdout/err to /dev/null using a shell or merge stdout/err with redirectErrorStream(true) and spawn another thread that reads from stdout of the process
You want the trick?
Don't start your process from ProcessBuilder.start(). Don't try to mess with stream redirection/consumption from Java (especially if you give no s**t about it ; )
Use ProcessBuilder.start() to start a little shell script that gobbles all the input/output streams.
Something like that:
#!/bin/bash
nohup $1 >/dev/null 2>error.log &
That is: if you don't care about stdout and still want to log stderr (do you?) to a file (error.log here).
If you don't even care about stderr, just redirect it to stdout:
#!/bin/bash
nohup $1 >/dev/null 2>1 &
And you call that tiny script from Java, giving it as an argument the name of the process you want to run.
If a process running on Linux that is redirecting both stdout and stderr to /dev/null still produce anything then you've got a broken, non-compliant, Linux install ;)
In other word: the above Just Works [TM] and get rid of the problematic "you need to consume the streams in this and that order bla bla bla Java-specific non-sense".
The thread running the process may block if it does not handle the output. This can be done by spawning a new thread that reads the output of the process.
final ProcessBuilder builder = new ProcessBuilder("script")
.redirectErrorStream(true)
.directory(workDirectory);
final Process process = builder.start();
final StringWriter writer = new StringWriter();
new Thread(new Runnable() {
public void run() {
IOUtils.copy(process.getInputStream(), writer);
}
}).start();
final int exitValue = process.waitFor();
final String processOutput = writer.toString();
Just stumbled on this after I had a similar issue. Agreeing with nos, you need to handle the output. I had something like this:
ProcessBuilder myProc2 = new ProcessBuilder(command);
final Process process = myProc2.start();
and it was working great. The spawned process even did output some output but not much. When I started to output a lot more, it appeared my process wasn't even getting launched anymore. I updated to this:
ProcessBuilder myProc2 = new ProcessBuilder(command);
myProc2.redirectErrorStream(true);
final Process process = myProc2.start();
InputStream myIS = process.getInputStream();
String tempOut = convertStreamToStr(myIS);
and it started working again. (Refer to this link for convertStreamToStr() code)
Edit: I have no concern over the I/O stream created from the Process, and have thus taken no steps to deal with that--could this cause a hang in the Process itself?
If you don't read the output streams created by the process then it is possible that the application will block once the application's buffers are full. I've never seen this happen on Linux (although I'm not saying that it doesn't) but I have seen this exact problem on Windows. I think this is likely related.
JDK7 will have builtin support for subprocess I/O redirection:
http://download.oracle.com/javase/7/docs/api/java/lang/ProcessBuilder.html
In the meantime, if you really want to discard stdout/stderr, it seems best (on Linux) to invoke ProcessBuilder on a command that looks like:
["/bin/bash", "-c", "exec YOUR_COMMAND_HERE >/dev/null 2>&1"]
Another solution is to start the process with Redirect.PIPE and close the InputStream like this:
ProcessBuilder builder = new ProcessBuilder(cmd);
builder.redirectOutput(Redirect.PIPE);
builder.redirectErrorStream(true); // redirect the SysErr to SysOut
Process proc = builder.start();
proc.getInputStream().close(); // this will close the pipe and the output will "flow"
proc.waitFor(); //wait
I tested this in Windows and Linux, and works!
In case you need to capture stdout and stderr and monitor the process then using Apache Commons Exec helped me a lot.
I believe the problem is the buffering pipe from Linux itself.
Try to use stdbuf with your executable
new ProcessBuilder().command("/usr/bin/stdbuf","-o0","*executable*","*arguments*");**
The -o0 says not to buffer the output.
The same goes to -i0 and -e0 if you want to unbuffer the input and error pipe.
you need to read the output before waiting to finish the cycle. You will not be notified If the output doesn't fill the buffer. If it does, it will wait until you read the output.
Suppose you have some errors or responses regarding your command which you are not reading. This would cause the application to stop and waitFor to wait forever. A simple way around is to re-direct the errors to the regular output.
I was spent 2 days on this issue.
public static void exeCuteCommand(String command) {
try {
boolean isWindows = System.getProperty("os.name").toLowerCase().startsWith("windows");
ProcessBuilder builder = new ProcessBuilder();
if (isWindows) {
builder.command("cmd.exe", "/c", command);
} else {
builder.command("sh", "-c", command);
}
Process process = builder.start();
BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()));
String line;
while ((line = reader.readLine()) != null)
System.out.println("Cmd Response: " + line);
process.waitFor();
} catch (IOException e) {
e.printStackTrace();
} catch (InterruptedException e) {
e.printStackTrace();
}
}

Child Process will neither complete nor abort in Windows?

How to make my java parent process wait till child process gets completed. I have tried with runtime.exec and with processBuilder.pb:
String cmd = "ffmpeg -i input.vob output.mp4"
Runtime rt = Runtime.getRuntime();
Process proc = rt.exec(cmd);
proc.waitFor();
This works fine with small input file (say less than 10 Mb). If I give larger input file then program will be hanged. Output file will be partially created and file creation will be hanged and control will not return. Even proc.join(10000); did not give any useful result. Here parent process is terminating before child process (ffmpeg) gets completed.
How to overcome this problem?
You need to read the data outputted by the process.
A process has 2 output streams: standard output and error output. You have to read both because the process might write to both of those outputs.
The output streams of the process have buffers. If the buffer of an output stream is filled, attempt to write further data to that by the process is blocked.
Do something like this:
InputStream in = proc.getInputStream(); // To read process standard output
InputStream err = proc.getErrorStream(); // To read process error output
while (proc.isAlive()) {
while (in.available() > 0)
in.read(); // You might wanna echo it to your console to see progress
while (err.available() > 0)
err.read(); // You might wanna echo it to your console to see progress
Thread.sleep(1);
}

Running jar.exe, process.waitFor never return

I have java cde that jars class files together:
List<String> args = new ArrayList<String>();
String path = FileSystemUtils.JavaBin() + "\\jar.exe";
args.add(path);
args.add("-cfv");
args.add(jarName);
args.addAll(FileSystemUtils.getAllFiles(directory, ".class"));
ProcessBuilder pb = new ProcessBuilder(args);
File wd = new File(directory);
pb.directory(wd);
Process p = pb.start();
//Waiting for process to exit
p.waitFor();
int res = p.exitValue();
Tis code works great.
However, on some computers - not on all of them, when there are 7+ files, the p.waitFor(); never return, even though the jar was created.
Looking at the task manager, jar.exe really did not terminate.... what can be the cause?
running the same command manually from the command line exits immediately.
This seems very weird. Does someone have any hint?
Found the solution myself.
Apparently if you use ProcessBuilder.start in Java to start an external process you have to consume its stdout/stderr, otherwise the external process hangs.
This is beacuase OS creates a pipe.
All Unix like OSs and Windows behave the same in this regard: A pipe with a 4K is created between parent and child. When that pipe is full (because one side isn't reading), the writing process blocks.
It seems that when there are 7+ files the jar.exe consume 4K, nad then stuck.
Javadoc of process:
By default, the created subprocess 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 subprocess. 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 subprocess may cause the subprocess
to block, or even deadlock.

Creating a ".bat" file, run it and delete it from Java

I'm making a .bat dynamically in Java.
After creating the file and exectuing it, I want to delete it.
In order to make sure that delete() will be executing after running the .bat file, I delete the file within a different thread.
Is this a good approach for this task? How would you implement it in a better way?
final File file = new File("run.bat");
file.createNewFile();
PrintWriter writer = new PrintWriter(file, "UTF-8");
writer.println(COMMAND1);
writer.println(COMMAND2);
.... ANOTHER COMMANDS
writer.close();
Runtime.getRuntime().exec("cmd /c start a.bat");
new Thread(new Runnable() {
public void run() {
file.delete();
}
}).run();
I wouldnt delete the file in a different thread, what if the file execution is still running at the time you try to delete it?
I would consider asking the exit code from the file, or use a process.waitFor() which causes the current thread to wait, if necessary, until the process represented by this Process object has terminated.
Process p = Runtime.getRuntime().exec("cmd /c start a.bat");
p.waitFor();
file.delete();
Try this which causes the code to wait until the batch file execution is complete before deleting the file.
Process batchFileExecutionProcess = Runtime.getRuntime().exec("cmd /c start a.bat");
batchFileExecutionProcess.waitFor();
file.delete();
Ideally, you would build error handling into this solution as well, such as looking for a 0 return from the waitFor() method.
No, it's not good approach. Actually I don't see any advantage of the extra thread. Most likely it will attempt to delete the file while it's still being used by cmd and deletion will fail.
Instead you should use Process object (the result of exec()) and wait until the it finished and delete the file then:
Process p = Runtime.getRuntime().exec("cmd /c start a.bat");
p.waitFor();
file.delete()

Wait until DOS command execution has finished - Java

I am trying to import a large amount of .dmp files in a MySQL DB and since there are more than 250 files that have to be imported I wrote an app to automate the execution of the 250+ DOS commands. The code for it:
String baseCommand = "cmd /c MySQL -h localhost -u root amateurstable < ";
Process p = Runtime.getRuntime().exec(baseCommand + filePath);
It does execute the commands it is supposed to. The problem is that some of the .dmp files are larger than 100MB, but the code above does not wait until the execution of the command is finished.
When it executes the import command for a large file it does not wait until the import is over and executes the next command right after. This causes a lot of headaches in terms of responsiveness of the computer.
The question is how to make it wait until the execution of the command completes?
Runtime.exec returns a Process object that has a waitFor() method.
waitFor()
causes the current thread to wait, if necessary, until the process represented by this Process object has terminated.
Since you already have that Process object you could just add the call to waitFor()
Process p = Runtime.getRuntime().exec(baseCommand + filePath);
p.waitFor();
You could use ProcessBuilder:
ProcessBuilder pb = new ProcessBuilder(baseCommand + filePath, "");
Process start = pb.start();
start.waitFor();
use process.waitFor()
Have you looked at Apache Commons Exec as well?
http://commons.apache.org/exec/tutorial.html
Seems like you have your processing well in hand, but I think it's a little easier to work with as a wrapper over the Runtime exec. If you needed to kill a process it can make life easier.

Categories

Resources