You would think that launching a bat file from Java would be an easy task but no... I have a bat file that does some sql commands for a loop of values read from a text file. It is more or less like this:
FOR /F %%x in (%CD%\listOfThings.txt) do sqlcmd -Slocalhost\MSSQL %1 %2 -d %3 -i %CD%\SQLScripts\\%%x
exit
Don't worry about the specifics they are not important. What i want is to simply run this bat file from within Java and have it wait until execution is finished. Apparently it is not easy. What i have so far is this:
Runtime.getRuntime().exec("cmd /K start SQLScriptsToRun.bat"
+" -U"+getUser()
+" -P"+getPass()
+" " + projectName);
return true;
The problem is that the exec() method returns immediately. The bat file runs for a good 2-3 minutes. I tried removing the start but to no avail. I tried many variations but it got me nowhere. Any ideas on how to do this simple task?
You should not ignore the return value of .exec(). It gives you a Process object that you can waitFor(), like this:
final Process process = Runtime.getRuntime().exec("blahblahblah");
final int exitVal = process.waitFor();
// if exitVal == 0, the command succeeded
you need to use waitFor on the process exec call returns.
Related
I have a code that execute an external program. And now I need that my application wait the end of the execution of that external program.
But I'm not shure how I supposed to do that. I tried some things but don't work.
public Image acquireImage() throws IOException, InterruptedException {
process = Runtime.getRuntime().exec("cmd.exe /c start "+ApplicationProperties.getPath()
+ "\\.wimdesktop\\Release\\Static_GenerateGain.exe");
process.waitFor();
System.out.println("EXIT: " + process.exitValue());
return copyImage();
}
The problem is that the System.out.println("EXIT: " + process.exitValue()); print 0 but the external program still running.
You are running cmd.exe and asking it to start a process in the background. So all you are seeing is cmd.exe exit status 0 after it launches your app - and that app may still be running.
If you want waitFor to apply to the sub-process just run the exe directly without the launch wrapper:
process = Runtime.getRuntime().exec(ApplicationProperties.getPath()
+ "\\.wimdesktop\\Release\\Static_GenerateGain.exe");
Note that if your EXE depends on environment variables set by CMD.EXE then you may need to try your original command without "start" for background process:
process = Runtime.getRuntime().exec("cmd.exe /c "+ApplicationProperties.getPath()
+ "\\.wimdesktop\\Release\\Static_GenerateGain.exe");
In both cases above you may run into second issue that the command freezes, this is because you are not reading the Stdout and error streams. There are many SO posts on how to do this.
How do I start processes from a script in a way that also allows me to terminate them?
Basically, I can easily terminate the main script, but terminating the external processes that this main script starts has been the issue. I googled like crazy for Perl 6 solutions. I was just about to post my question and then thought I'd open the question up to solutions in other languages.
Starting external processes is easy with PerlĀ 6:
my $proc = shell("possibly_long_running_command");
shell returns a process object after the process finishes. So, I don't know how to programmatically find out the PID of the running process because the variable $proc isn't even created until the external process finishes. (side note: after it finishes, $proc.pid returns an undefined Any, so it doesn't tell me what PID it used to have.)
Here is some code demonstrating some of my attempts to create a "self destructing" script:
#!/bin/env perl6
say "PID of the main script: $*PID";
# limit run time of this script
Promise.in(10).then( {
say "Took too long! Killing job with PID of $*PID";
shell "kill $*PID"
} );
my $example = shell('echo "PID of bash command: $$"; sleep 20; echo "PID of bash command after sleeping is still $$"');
say "This line is never printed";
This results in the following output which kills the main script, but not the externally created process (see output after the word Terminated):
[prompt]$ ./self_destruct.pl6
PID of the main script: 30432
PID of bash command: 30436
Took too long! Killing job with PID of 30432
Terminated
[prompt]$ my PID after sleeping is still 30436
By the way, the PID of sleep was also different (i.e. 30437) according to top.
I'm also not sure how to make this work with Proc::Async. Unlike the result of shell, the asynchronous process object it creates doesn't have a pid method.
I was originally looking for a Perl 6 solution, but I'm open to solutions in Python, Perl 5, Java, or any language that interacts with the "shell" reasonably well.
For Perl 6, there seems to be the Proc::Async module
Proc::Async allows you to run external commands asynchronously, capturing standard output and error handles, and optionally write to its standard input.
# command with arguments
my $proc = Proc::Async.new('echo', 'foo', 'bar');
# subscribe to new output from out and err handles:
$proc.stdout.tap(-> $v { print "Output: $v" });
$proc.stderr.tap(-> $v { print "Error: $v" });
say "Starting...";
my $promise = $proc.start;
# wait for the external program to terminate
await $promise;
say "Done.";
Method kill:
kill(Proc::Async:D: $signal = "HUP")
Sends a signal to the running program. The signal can be a signal name ("KILL" or "SIGKILL"), an integer (9) or an element of the Signal enum (Signal::SIGKILL).
An example on how to use it:
#!/usr/bin/env perl6
use v6;
say 'Start';
my $proc = Proc::Async.new('sleep', 10);
my $promise= $proc.start;
say 'Process started';
sleep 2;
$proc.kill;
await $promise;
say 'Process killed';
As you can see, $proc has a method to kill the process.
Neither Perl, Perl 6, nor Java, but bash:
timeout 5 bash -c "echo hello; sleep 10; echo goodbye" &
In Java you can create a process like this:
ProcessBuilder processBuilder = new ProcessBuilder("C:\\Path\program.exe", "param1", "param2", "ecc...");
Process process = processBuilder.start(); // start the process
process.waitFor(timeLimit, timeUnit); // This causes the current thread to wait until the process has terminated or the specified time elapses
// when you want to kill the process
if(process.isAlive()) {
process.destroy();
}
Or you can use process.destroyForcibly();, see the Process documentation for more info.
To execute a bash command point to the bash executable and set the command as a parameter.
In my java program I am trying to run a different program through CMD with its output appearing in a command window in the foreground and then analyze the exit code of the child program (foo) in the main java program. Unfortunately, all I seem to be able to access is the exit code of the CMD window, which is always 0.
The following is a snippet of what I'm doing:
ProcessBuilder pb = new ProcessBuilder();
pb.directory(new File(dir));
pb.command("cmd","/c","start","/wait","foo.exe",arg);
process = pb.start();
exitVal = process.waitFor();
but exitVal is always 0 regardless of how foo exits. How can I get just the exit code of foo?
I'm also pretty new to java so if there's a more elegant way of doing this, I'm open to suggestions.
I found a solution by modifying one of the things I had tried before to account for windows batch being finicky. The solution was to send another command to cmd to tell it to exit with the most recent error code. Earlier I had tried this by appending & exit %errorlevel% to the command but cmd variables are only updated at the end of each command line, not at the end of each command.
To force update, I used %^errorlevel% instead.
I write the code in Java to launch specific application via command prompt. So the code will open cmd and run the application from there. I want to get the errorlevel of the execution. I tried with
String errorlevel = System.getenv("errorlevel");
System.out.println(errorlevel);
It always returns zero either the execution is failed or not. I also tried with getProperty() and came up with the same result.
Then the other way I tried
Process p = Runtime.getRuntime().exec(command);
int i = p.waitFor();
System.out.println("errorLevel :" + ""+ i);
With this, i get the exitValue but not exactly what I want since I have to close the application to retrieve the value
What I want to know is whether it is possible to get the errorlevel of commandprompt without exit/close the process manually
Thanks before
I am trying to automate some processes that were build in ancient times, for the sake of avoiding repetitive actions. It is required that the processes are started with one batch and stopped with another (this can not be changed btw).
So i made a commandline tool to do this (and many other repetitive stuff) and I have modelled a command that starts the 'startbatch' and a command that start the 'stopbatch'. Both commands work fine separatly (as I tested them separatly) but there seems to be a problem when i want execute them one after another (in the correct order ofcourse). I get the following error in new cmd.exe window:
The process cannot access the file because it is being used by another process.
the code that i am using to start the batches looks like this:
public void startBatchInDev(String company){
String startBatchFolder = locations.getLocationFor("startbatch");
try{
Runtime runtime = Runtime.getRuntime();
runtime.exec("cmd.exe /C cd \"" + startBatchFolder + "\" & start cmd.exe /k \"" + BATCHSTART + company.toLowerCase()+ "-dev" + BATCH_SUFFIX + "\"");
}
catch(IOException ioe){
ioe.printStackTrace();
}
}
public void stopBatchInDev(String company){
String startBatchFolder = locations.getLocationFor("startbatch");
try{
Runtime runtime = Runtime.getRuntime();
runtime.exec("cmd.exe /C cd \"" + startBatchFolder + "\" & start cmd.exe /k \"" + BATCHSTOP + company.toLowerCase()+ "-dev" + BATCH_SUFFIX + "\"");
}
catch(IOException ioe){
ioe.printStackTrace();
}
}
The names of the batchfiles are concatenated, but they are OK once the application is running.
The error message is quite clear, some file is locked and I can't access it because of it. Some googling confirms my suspicion, but I can't seem to find a solution for this. The hits in google are all about obvious uses of files, like an obvious shared resource. But in my case, i am not working on the same batch file. The stop and start batch are two different files. So I am actually starting to think that it might be the cmd.exe file that is being locked by windows...
So this question is actually two questions:
- what is the exact cause of the described problem?
- how do i programmatically fix this (if possible)?
thanks in advance!
So, basically, bat is not so great :-(
I was able to repro this from java, but I also found that this script:
#echo off
echo STOP
echo STOP >> E:\tmp\java\logfile.txt
C:\cygwin\bin\sleep.exe 1
echo STOP1 >> E:\tmp\java\logfile.txt
C:\cygwin\bin\sleep.exe 1
echo STOP2 >> E:\tmp\java\logfile.txt
When run twice like this:
start test.bat && start test.bat
Will fail with one or more messages like:
The process cannot access the file because it is being used by another process.
The reason is that " >> " redirection opens the file for Read/Write access but only FILE_SHARE_READ sharing. If two different programs attempt to open the file this way, one of them fails.
So, you cannot have two different batch files running at the same time and logging to the same file