I'm trying to execute a Batch Script with ProcessBuilder and can't figure out why it's not working.
I built a small PoC to show my problem, in order for this to work you would need to create some folders on C Drive:
22840c1a
22840c1a\subfolder
Then copy your calc.exe into the subfolder. Next create start.bat inside of 22840c1a. Paste the following content into start.bat
@echo off
echo "Set WorkingDirectory"
cd /d C:\22840c1a\subfolder
echo "Start"
C:\22840c1a\subfolder\calc.exe
With that you're able to run the following unit test and reproduce the problem:
Update: Added working PoC
import org.junit.jupiter.api.Test;
import java.io.File;
import java.io.IOException;
import java.util.Arrays;
import java.util.List;
class BatchExecutionTest {
@Test
void pocWorking() {
execute(Arrays.asList("C:\\22840c1a\\start.bat"), "C:\\22840c1a");
}
@Test
void pocNotWorking() {
execute(Arrays.asList("start.bat"), "C:\\22840c1a");
}
@Test
void pocWorkingCmd1() {
execute(Arrays.asList("cmd", "/c", "C:\\22840c1a\\start.bat"), "C:\\22840c1a");
}
@Test
void pocWorkingCmd2() {
execute(Arrays.asList("cmd", "/c", "start.bat"), "C:\\22840c1a");
}
public static void execute(List<String> commands, String workingDirectory) {
try {
final ProcessBuilder pb = new ProcessBuilder(commands);
pb.redirectErrorStream(true);
pb.directory(new File(workingDirectory));
pb.start();
} catch (IOException e) {
throw new IllegalStateException(e);
}
}
}
If everything works as expected the Unit Test is not able to execute start.bat because the File could not be found. However you can run start.bat from cmd and its working fine. Why is ProcessBuilder not able to execute the Batch Script?
The ProcessBuilder
command line launch using a relative path as you've used in pocNotWorking()
isn't implemented as you think.
ProcessBuilder
is applying a normal search using Path
of your JVM. It does not - rightly or wrongly - consider checking in proposed subprocess workingDirectory
to resolve a relative pathname to the command.
You can verify this by appending C:\22840c1a
to the path BEFORE launching your JVM for the unit tests, and then I would expect all 4 tests to work:
set Path=%Path%;C:\22840c1a
I also tested pocWorking/pocNotWorking
on Linux with a shell script start.sh
and it gives consistent results to Windows when calling pocWorking/pocNotWorking
. The relative path version won't run unless you adjust PATH beforehand so that start.sh
is resolved in PATH rather than the working directory:
export PATH=$PATH:/somepathto/22840c1a
Note that you cannot fix this by attempting to edit path for the subprocess via pb.environment().set("Path", xxx)
, the path edit must be to the JVM parent process.
Overall, it seems good practice to use absolute path to ProcessBuilder
or resolve command as workingDirectory + File.separator + relativeExeName