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

标签 java windows batch-file cmd process

这一定是我观察到的最奇怪的事情之一。考虑以下 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 复制到桌面上的某个位置并更改路径会导致所有内容正确终止
  • A nearly equivalent program使用相同的命令在 Go 中工作正常
  • 我得到 this output如果我在 WinDbg 中运行所有内容并在它挂起后中断进程
  • 这似乎无法通过 MSVC2013 的 vcvarsall.bat 重现,但也可以通过 Windows 10 上的 MSVC2015 重现

原来的程序到底出了什么问题?如果我将整个命令 (cmd/c "C:\...) 复制并粘贴到开始-> 运行中,它会立即启动 cmd 并按预期终止。

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

最佳答案

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

这是您代码中的错误。 :-)

默认情况下,使用 ProcessBuilder 对象创建的子进程会将输出重定向到管道,管道的父端可以使用 Process.getInputStream() 获取,如果您代码不使用它。

由于您的代码只是简单地调用了 .waitFor 而没有做任何排空管道的准备,一旦管道的缓冲区溢出,它就会死锁。我相信默认的缓冲区大小是 4,096 字节。在我的机器上,您正在运行的命令的输出是 5,192 字节,但这将根据环境 block 的原始内容而有所不同。 (从它的声音来看,您的环境中的输出长度是临界值,仅略高于限制,因此即使是像更改 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());
        }
    }
}

关于java - cmd.exe 意外挂起,具体取决于我使用的文件所在的位置,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/43341086/

相关文章:

ios - 在 Windows 中打开 Xcode 项目

batch-file - 批处理文件夹和文本文件创建

java - 是否可以将 Spring Security 的 AOP 与 Java Security Manager 合并?

java - 在Java中更改文件路径的字符串

windows - 您如何构建针对 Windows 的 perl 源代码?

python - 如何升级pip?

batch-file - “if an ' exe 的批处理文件'未在(任务管理器)中运行,然后执行此任务”?

windows - 从批处理文件中的不同目录运行时 VBS 文件卡住

java - 什么是绘画和重绘功能的替代方案?

java - 将 Android 短信对话标记为已读。 Sql 查询一次又一次执行。(错误)