I am working on a program that I want to run a console command and save the output to a string. This program is installed by the user before running my program.
However, I have found I keep getting the error Cannot run program "XXXX" (in directory "C:\Users\accou\Downloads"): CreateProcess error=2, The system cannot find the file specified
For example, I have the program "Maui" that I have added to my system path:
Running in my Command Prompt:
C:\Users\accou\Downloads>maui
HELLO
C:\Users\accou\Downloads>
If I run the following code in Java I get the exception above
ProcessBuilder pb =
new ProcessBuilder("maui");
pb.directory(new File("C:\\Users\\accou\\Downloads"));
File log = new File("C:\\Users\\accou\\Downloads\\log.txt");
pb.redirectErrorStream(true);
pb.redirectOutput(ProcessBuilder.Redirect.appendTo(log));
Process p = pb.start();
assert pb.redirectInput() == ProcessBuilder.Redirect.PIPE;
assert pb.redirectOutput().file() == log;
assert p.getInputStream().read() == -1;
Exception:
Exception in thread "main" java.io.IOException: Cannot run program "maui" (in directory "C:\Users\accou\Downloads"): CreateProcess error=2, The system cannot find the file specified
at java.base/java.lang.ProcessBuilder.start(ProcessBuilder.java:1142)
at java.base/java.lang.ProcessBuilder.start(ProcessBuilder.java:1073)
at Application.main(Application.java:13)
Caused by: java.io.IOException: CreateProcess error=2, The system cannot find the file specified
at java.base/java.lang.ProcessImpl.create(Native Method)
at java.base/java.lang.ProcessImpl.<init>(ProcessImpl.java:483)
at java.base/java.lang.ProcessImpl.start(ProcessImpl.java:158)
at java.base/java.lang.ProcessBuilder.start(ProcessBuilder.java:1109)
... 2 more
I will get the same error if I try commands such as "dir" which I know 100% is available in windows.
However, if I can the command to the following it workes perfectly fine.
new ProcessBuilder("cmd.exe", "/c", "maui");
The following also works
new ProcessBuilder("maui.cmd");
Why is it that I cannot run "dir" or more importantly "maui" directly?
You're confusing ProcessBuilder with a shell. ProcessBuilder is not 'ask the dos box cmd shell to act as if we ran this statement', and when you type something in that black box, such as 'dir', that is not the same as 'ask the OS to execute this process'.
The shell does all sorts of intriguing transformations and interpretations of what you typed. That's cmd.exe (or, on other OSes, e.g. /usr/bin/bash), it is not the OS doing that, and java does not ship with a bash or cmd.exe baked in. It just asks the OS to do what you asked.
Thus, you can't use all these shellisms. Unfortunately, java tries to do some very basic shellisms, which just adds confusion. In particular:
*.txt
and friends probably works. On other OSes it probably does not, as expanding a star into all matching files is a bashism. Best not use it.cd
, pwd
, test
, dir
, and others definitely do not work.foo.exe >PRN
or /usr/bin/whatnot >/dev/printer
. You can't write an if
or a loop, or use %foo%
or $foo
or any other such things.This leads to the following conclusion:
cmd.exe /c C:\absolute\path\to\something.bat
, or on posixy systems, /bin/bash -c /abs/path/to/shellscript
, and strongly consider on windows to use System.getenv
to get the installed location of windows, so you can absolute-path cmd to thatPath + "cmd.exe"
, so that you end up with "C:\\Windows\\cmd.exe"
or equivalent (windows may not actually be installed there, hence, use env).Unwieldy? Yeah. Don't use ProcessBuilder unless you know what you are doing.
In this case, if type maui
on the command line, it's cmd.exe that figures out: Oh, hey, there is a maui.cmd
in this dir on the path, surely they meant that. That's a shellism. Not baked into the OS itself, and java does not ship with cmd.exe built in, so that does not work. dir
is not an executable but a built-in feature of cmd.exe. Same rule.
Java can do all these baked in things. There is no need to call dir
- java can walk paths and give you all relevant info with e.g. the java.nio.file.Files
API.