这必须是我曾经观察过的最奇怪的事情之一。请考虑以下Java程序:
import java.io.IOException;
public class StrangeError {
public static void main(String[] args) {
try {
Process process = new ProcessBuilder(
"cmd",
"/c",
"\"C:\\Program Files (x86)\\Microsoft Visual Studio 14.0\\VC\\vcvarsall.bat\" amd64 && set"
).start();
process.waitFor();
} catch (IOException|InterruptedException e) {
System.out.println(e.getMessage());
}
}
}
我使用javac StrangeError.java
对其进行了编译,将其复制到运行Windows Server 2012 R2的服务器上,然后使用java StrangeError
进行运行。
这里的事情开始变得怪异。该程序挂起,等待它产生的过程完成。这不是预期的行为,因为vcvarsall.bat
脚本应该立即完成,set
。
所以我开始玩游戏并发现以下内容:
set
会导致vcvarsall.bat
终止vcvarsall.bat
会导致set
终止&&
替换||
会导致所有内容正确终止vcvarsall.bat
复制到桌面上的某个位置并更改路径会导致所有内容正确终止vcvarsall.bat
不可重现,但在Windows 10上也可与MSVC2015重现原始程序究竟出了什么问题?如果我将整个命令(cmd /c "C:\...
)复制并粘贴到Start-> Run中,它会立即启动cmd
并按预期终止。
这是Java的错误吗?这是Windows的错误吗?
答案 0 :(得分:6)
这是Java的错误吗?这是Windows的错误吗?
这是您代码中的错误。 :-)
默认情况下,使用ProcessBuilder对象创建的子进程将输出重定向到管道,其父结束可以使用Process.getInputStream()
获取,如果您的代码没有使用它,则不会自动耗尽
由于您的代码只是调用.waitFor
而没有任何排除管道的规定,因此一旦管道缓冲区溢出,它就会死锁。我相信默认缓冲区大小是4,096字节。在我的机器上,您运行的命令的输出是5,192字节,但这将根据环境块的原始内容而有所不同。 (从它的声音来看,你的环境中的输出长度是临界的,只是在极限之上,所以即使像改变VS的版本这样的小变化也会产生影响。)
根据您实际尝试做的事情,许多可能的解决方案之一是告诉Java不要管道孩子的输出:
import java.io.IOException;
public class StrangeError {
public static void main(String[] args) {
try {
ProcessBuilder processb = new ProcessBuilder(
"cmd",
"/c",
"\"C:\\Program Files (x86)\\Microsoft Visual Studio 14.0\\VC\\vcvarsall.bat\" amd64 && set"
);
processb.redirectOutput(ProcessBuilder.Redirect.INHERIT);
Process process = processb.start();
process.waitFor();
} catch (IOException|InterruptedException e) {
System.out.println(e.getMessage());
}
}
}
答案 1 :(得分:0)
无法在同一ProcessBuilder中读取标准输入和输出错误。
所以你需要创建两个ProcessBuilder
Process process1 = new ProcessBuilder(
"cmd",
"/c",
"\"C:\\Program Files (x86)\\Microsoft Visual Studio 14.0\\VC\\vcvarsall.bat\",
"amd64");
Process process2 = new ProcessBuilder(
"cmd",
"/c",
"set");
process1.start();
if (process1.waitFor() == 0) {
process2.start();
if (process2.waitFor() == 0) {
// Successfull execution
}
}
还有一件事:我不认为用Java(或其他语言)进行shell /批量启动是一个好习惯。也许您应该使用脚本(shell,batch,python,perl ...)来控制标准输入/输出流。