cmd.exe意外挂起,具体取决于我使用的文件所在的位置

Nat*_*man 5 java windows cmd process batch-file

这必须是我曾经观察过的最奇怪的事情之一.考虑以下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());
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

我编译它javac StrangeError.java,将其复制到运行Windows Server 2012 R2的服务器上,然后运行它java StrangeError.

事情开始变得奇怪了.该程序挂起,等待它产生的过程完成.这不是预期的行为,因为vcvarsall.bat脚本应该立即完成set.

所以我开始玩游戏并发现以下内容:

  • 删除set原因vcvarsall.bat终止
  • 删除vcvarsall.bat原因set终止
  • 更换&&||原因的一切正确终止
  • 复制vcvarsall.bat到桌面上的某个位置并更改路径会导致所有内容正确终止
  • 一个几乎相同的程序使用相同的命令中转到正常工作
  • 如果我在WinDbg中运行所有内容并在挂起后中断该过程,我会得到此输出
  • 这似乎不是可以vcvarsall.bat从MSVC2013 重现,但也可以在Windows 10上与MSVC2015重现

原计划究竟出了什么问题?如果我将整个命令(cmd /c "C:\...)复制并粘贴到Start-> Run中,它会立即启动 cmd并终止,如预期的那样.

这是Java的错误吗?这是Windows的错误吗?

Har*_*ton 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());
        }
    }
}
Run Code Online (Sandbox Code Playgroud)