带有/不带cmd.exe的Java子进程命令行执行

时间:2016-02-23 17:31:46

标签: java windows cmd subprocess

我的问题包含了一些我不了解的使用" cmd.exe"," / c"在Windows中从Java执行子流程时。基本上,我无法找到关于何时以及为何需要它们的好解释。

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

new ProcessBuilder("java", "...").start();

原因:

Could not find or load main class ...

好像系统变量或目录不同(他们不是)。另一方面,将其包装在cmd.exe中,如下所示:

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

WORKS,但是创建了另一个cmd.exe进程,它有一个副作用:子JVM现在是一个子子进程,process.destroy();没有杀死它(一个已知的bug)我找到的Windows JRE)。

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

这里框架本身也是如此。它也将被我们的测试平台使用。我想提供一个API,允许通过参数用cmd.exe / c包装命令。但是,该参数究竟是什么意思呢?用户如何决定是否需要cmd.exe包装?

我非常感激的奖励:在其他操作系统中是否有任何相关性?比方说,它在Linux中有某种等价物吗?

1 个答案:

答案 0 :(得分:2)

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

@HarryJohnston - 好的一点,根据javadoc,子进程从ProcessBuilderRuntime.getRuntime.exec(...) API继承了环境,所以这不是问题(事实上java.exe实际上是这样发现的)显然是可用的。)

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

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

基本上:

java -cp %classpath% Main - 糟糕

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

java -cp D:\proj\bin Main - 好

那么,关于一般问题的行为与cmd.exe的行为有何不同:

  1. 这样的变量引用:%VARIABLE%仅由cmd.exe解释。
  2. 使用cmd.exe你真的不需要将命令拆分为String数组参数,你可以使用一个长字符串(“cmd.exe”,“/ c”,“其余......”数组) )。
  3. 子进程将是cmd.exe而不是实际的可执行文件,因此process.destroy()不会将其删除。这可能是以后版本中修复的错误(我使用的是Java 7和8,Windows 7和Server 2012)。
  4. 文件关联仅适用于cmd.exe,因此您无法“执行”不是程序或脚本的文件(“CreateProcess error = 193,%1不是有效的Win32应用程序”)。调用PATH中定义的可执行文件确实有效。
  5. startcall仅在cmd.exe下可用。
  6. 命令级别错误的行为也不同:使用cmd.exe时,错误只会出现在输出流中,而如果没有它,则API会抛出异常,这可能也会略有错误不同的描述。

    例如:

    nothing会导致 java.io.IOException:CreateProcess error = 2,系统找不到指定的文件

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

  7. 关于为什么的更深层次的问题,这是完整的差异列表,我仍然不知道。看起来JVM有运行命令的方式,您可以使用cmd.exe“包装”其他功能和保护。

    另一个有趣的事实是,当您运行批处理时,它实际上是在cmd.exe(具有其所有功能)下运行的。从我的测试来看,似乎process.destroy()只能等待外部任何东西(例如,如果卡在pause上),有或没有额外的cmd.exe / c包装,但是如果它运行则不会子进程 - 与第3点中提到的cmd.exe限制一致。