Java Process Builder 运行 本地安装的程序

Java Process Builder Run locally installed program

我正在开发一个程序,我想 运行 一个控制台命令并将输出保存到一个字符串。该程序是用户在运行安装我的程序之前安装的。 但是,我发现我一直收到错误 Cannot run program "XXXX" (in directory "C:\Users\accou\Downloads"): CreateProcess error=2, The system cannot find the file specified 例如,我将程序“Maui”添加到我的系统路径中: 运行 在我的命令提示符中:

C:\Users\accou\Downloads>maui
HELLO

C:\Users\accou\Downloads>

如果我运行下面的代码在Java我得到上面的异常

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 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

如果我尝试诸如“dir”之类的命令,我知道 100% 在 windows 中可用,我会得到同样的错误。

但是,如果我可以执行以下命令,它就可以正常工作。

new ProcessBuilder("cmd.exe", "/c", "maui");

以下也有效

new ProcessBuilder("maui.cmd");

为什么我不能直接运行“dir”或更重要的“maui”?

您将 ProcessBuilder 与 shell 混淆了。 ProcessBuilder 不是 'ask the dos box cmd shell to act as if we ran this statement',当您在该黑框中键入内容时,例如 'dir',它与 'ask the OS to execute this process' 不同。

shell 对您输入的内容进行各种有趣的转换和解释。那是 cmd.exe(或者,在其他 OSes 上,例如 /usr/bin/bash),它是 而不是 OS 这样做 ,并且 java 不附带 bash 或 cmd.exe。它只是要求 OS 执行您的要求。

因此,您不能使用所有这些 shell主义。不幸的是,java 试图做一些非常基本的 shellisms,这只会增加混乱。特别是:

  1. 单字符串参数变体将按空格拆分以尝试将可执行文件提取到 运行 + 要传递的参数。
  2. 在 windows 上,*.txt 和朋友可能会工作。在其他 OSes 上它可能不会,因为将星号扩展到所有匹配的文件是一种 bashism。最好别用。
  3. 内置的,如cdpwdtestdir等肯定不行。
  4. 依赖 $PATH 可能行不通,但这是 java 有时会尝试应用的。不过,最好不要依赖它。
  5. 任何花哨的重定向等都是 shell主义,是行不通的。您不能使用 foo.exe >PRN/usr/bin/whatnot >/dev/printer 要求 java 重定向到打印机。您不能编写 if 或循环,也不能使用 %foo%$foo 或任何其他类似的东西。

由此得出以下结论:

  1. 切勿对与 ProcessBuilder 相关的任何内容使用相对路径。
  2. 永远不要试图将可执行文件和参数压缩到一个字符串中;始终使用多字符串变体。
  3. 不要依赖绝对路径和绝对参数以外的任何东西。
  4. 如果您需要任何 shell 任何类型的主义,运行 cmd.exe /c C:\absolute\path\to\something.bat,或在 posixy 系统上,/bin/bash -c /abs/path/to/shellscript,并强烈考虑 windows使用 System.getenv 获取 windows 的安装位置,因此您可以使用绝对路径 cmd 到 thatPath + "cmd.exe",这样您就可以得到 "C:\Windows\cmd.exe" 或等效的(windows 可能实际上没有安装在那里,因此,使用 env).

笨重?是的。除非您知道自己在做什么,否则不要使用 ProcessBuilder。

在这种情况下,如果在命令行中输入maui,则cmd.exe会计算出:哦,嘿,路径上的这个目录中有一个maui.cmd,他们肯定是这个意思。那是一种 shell 主义。没有融入 OS 本身,并且 java 没有内置 cmd.exe,所以这不起作用。 dir 不是可执行文件,而是 cmd.exe 的内置功能。相同的规则。

Java 可以做所有这些内在的事情。无需调用 dir - java 可以走路径并为您提供所有相关信息,例如java.nio.file.Files API.