使用/不使用cmd.exe执行Java子进程命令行

使用/不使用cmd.exe执行Java子进程命令行,java,windows,cmd,subprocess,Java,Windows,Cmd,Subprocess,我的问题包括在Windows中从Java执行子进程时使用“cmd.exe”和“/c”的一些我不理解的地方。基本上,我找不到一个很好的解释来说明何时以及为什么需要它们。 我的具体问题是:我有一个执行子流程的小框架。一个用途是Java应用程序,它“管理”ProcessBuilders创建的其他几个JVM。其中一个关键要求是,当子进程卡住或宿主应用程序终止时,它必须能够终止子进程。 问题是,一方面,这样做: new ProcessBuilder("java", "...").start(); 原因如

我的问题包括在Windows中从Java执行子进程时使用“cmd.exe”和“/c”的一些我不理解的地方。基本上,我找不到一个很好的解释来说明何时以及为什么需要它们。

我的具体问题是:我有一个执行子流程的小框架。一个用途是Java应用程序,它“管理”ProcessBuilders创建的其他几个JVM。其中一个关键要求是,当子进程卡住或宿主应用程序终止时,它必须能够终止子进程。
问题是,一方面,这样做:

new ProcessBuilder("java", "...").start();
原因如下:

Could not find or load main class ...
好像系统变量或目录是不同的(它们不是)。另一方面,将其包装在cmd.exe中,如下所示:

new ProcessBuilder("cmd.exe", "/c", "java", "...").start();
可以工作,但会创建另一个cmd.exe进程,这有一个副作用:子JVM现在是一个子进程,
process.destroy()
并没有杀死它(我在Windows JRE中发现了一个已知的bug)。

这个特定的问题是在不同的级别上处理的,因为所有这些应用程序都是我们的,我们知道它们的PID。但这是一个cmd.exe如何使一切工作方式不同(或阻止JVM工作的例子)。所以我想知道那里到底发生了什么。

在这里,框架本身也出现在画面中。我们的测试平台也将使用它。我想提供一个API,它允许通过参数将命令包装为cmd.exe/c。但是,这个参数的确切含义是什么?用户如何决定是否需要cmd.exe包装?


还有一点我很感激:这与其他操作系统相关吗?它是否有某种等价物,比如在Linux中?

再次遇到它并发现了问题,以及一些额外的见解

@HarryJohnston-好的观点,根据javadoc,子进程从
ProcessBuilder
Runtime.getRuntime.exec(…)
API继承了环境,所以这不是问题所在(事实上找到了java.exe,所以路径显然是可用的)

但似乎某些命令行功能丢失了。除此之外,命令行中的%VARIABLE%用法——除非命令以
cmd.exe/c
开头,否则不会解释它们

在未找到类的情况下(它也可能导致NoClassDefFoundError),我在类路径中使用了几个变量(也许我应该发布整个命令,但它有点长)。当我用文学道路取代它们时,一切都起了作用

基本上:

java-cp%classpath%Main
-错误

cmd.exe/c java-cp%classpath%Main
-很好

java-cpd:\proj\bin Main
-好

因此,关于使用cmd.exe的行为有何不同的一般问题:

  • 像这样的变量引用:%Variable%仅由cmd.exe解释
  • 使用cmd.exe,您实际上不需要将命令拆分为参数的字符串数组,您可以使用一个长字符串(“cmd.exe”数组、“/c”、“其余…”)
  • 子进程将是cmd.exe,而不是实际的可执行文件,因此
    process.destroy()
    不会杀死它。这可能是在更高版本中修复的错误(我使用了Java7和Java8、Windows7和Server2012)
  • 文件关联仅适用于cmd.exe,因此不能“执行”非程序或脚本的文件(“CreateProcess error=193,%1不是有效的Win32应用程序”)。调用路径中定义的可执行文件确实有效
  • start
    call
    仅在cmd.exe下可用
  • 命令级错误的行为也有所不同:使用cmd.exe时,错误只会出现在输出流中,而如果没有它,API会抛出一个异常,该异常的描述也可能略有不同

    例如:

    无任何内容
    将导致java.io.IOException:CreateProcess error=2,系统无法找到指定的文件

    cmd.exe/c nothing
    将返回输出:“nothing”不被识别为内部或外部命令、可操作程序或批处理文件,返回值为1(唯一指示出错)

  • 关于为什么这个更深层次的问题,以及这是差异的全部列表,我仍然不知道。看起来JVM有其运行命令的方式,您可以使用cmd.exe“包装”这些命令以获得额外的功能和保护


    另一个有趣的事实是,当您运行批处理时,它实际上是在cmd.exe(及其所有功能)下运行的。从我的测试来看,只有当它没有等待任何外部信息时(例如,如果卡在
    pause
    上),无论是否附加cmd.exe/c包装,
    process.destroy()才能杀死它,但如果它运行一个子进程,则不会这样做,这与第3点中提到的cmd.exe限制一致。

    cmd/c将使用一些环境变量,如CLASSPATH和PATH,而直接调用java将从父进程获取环境。这可以解释行为上的差异。找出您的流程环境和cmd环境之间的差异。应用程序需要完全相同的变量,所以这很奇怪。顺便说一下,java。。。命令包含所需的变量。缺少封闭的cmd.exe是否会导致忽略它们?不管怎样,这只是一个例子,但这就是问题所在:这种包装到底有什么作用?环境、输出、返回值等。我还没有找到答案。根据我的经验,使用cmd的主要功能是变量替换和批处理文件支持(因此可以调用批处理文件而不是可执行文件)。调用批处理文件时不需要它(即使此批处理文件调用Java)@JPMoresmau:cmd.exe实例还将从父进程获取其环境变量,因此这不会造成任何差异