从Java程序调用批处理文件出现问题

3

我读到的所有资料都说,从Java程序中调用批处理文件的唯一方法是像这样做:

Process p = Runtime.getRuntime().exec("cmd /c start batch.bat");

据我所知,这将创建一个进程来运行CMD.exe,然后创建一个进程来运行批处理文件。但是,一旦实例化了批处理文件进程,CMD.exe进程似乎就会退出。

如何确认批处理文件在CMD进程退出之前已完成?


现在应该使用 ProcessBuilder 而不是 Runtime.getRuntime().exe() - OscarRyz
5个回答

2

Process p = Runtime.getRuntime().exec("cmd /c batch.bat");

如果你想等待批处理文件执行完毕后再进行下一步操作,需要将命令中的 "start" 去掉。"/c" 告诉 cmd.exe 在命令完成后返回,这是你想要的,但是 "start" 意味着启动一个新的 cmd.exe 进程来执行批处理文件。此时启动的 cmd.exe 已经完成并退出了,这可能不是你想要的。

以下两个代码片段中的任意一个都可以让批处理文件运行,并获取你所需的 Process 对象。

Process p = Runtime.getRuntime().exec("cmd /c batch.bat");

或者

ProcessBuilder pb= new ProcessBuilder ("cmd", "/c", "batch.bat");
    Process p = pb.start ();

现在你拥有了一个进程对象,你有多种等待批处理文件完成的方法。
最简单的方式是使用.waitFor()。
int batchExitCode = -1;
try {
  batchExitCode = p.waitFor ();
} catch (InterruptedException e) {
  // kill batch and re-throw the interrupt
  p.destroy (); // could also use p.destroyForcibly ()
  Thread.currentThread ().interrupt ();
}

你还可以使用waitFor(long timeout, TimeUnit unit)p.isAlive(),如果它们更符合你的需求的话。
警告:如果你的批量处理将向stdout和/或stderr输出大量数据(可能超过1024个字符,也可能更少),你的程序需要以某种方式处理这些数据,否则,程序与批处理进程之间的管道将填满,批处理文件将挂起等待管道中有新字符的空间,并且批处理文件将永远无法返回。
这是我这次最初来到Stack Overflow的问题。我有超过17,000条命令的批处理文件,每个命令生成300多个字符到stdout并且执行命令时产生的任何错误都会生成1000多个字符。
解决这个问题的一些方法:
  1. @echo off as the 1st line in the batch file, with this the cmd process will not echo each command in the batch file. If your batch file does not generate any other output to stdout or stderr you are done.

  2. If one or more of the commands in the batch file can or may generate a lot of stdout and / or stderr output then you have to handle that data yourself.

  3. If you are using "Runtime.getRuntime().exec" you only have one way to handle this and that is to get the InputStreams for the batch file's stdout and stderr by calling:

        InputStream outIS = p.getInputStream (); // this gets the batch file's stdout
        InputStream errIS = p.getErrorStream (); // this gets the batch file's stderr
    

    Now you have to read each of the InputStreams (only when they have data or you will wait until you do). Assuming that you get a lot more data from one of the InputStreams than the other you are almost assured of hanging the batch file. There are ways to read multiple streams without hanging, however, they are beyond the scope of my already excessively long answer.

  4. If you are using ProcessBuilder pb= new ProcessBuilder ("cmd", "/c", "batch.bat"); (and this is a major reason to do so) you have a lot more options available to you since you can ask for stdout and stderr to be combined pb.redirectErrorStream (true);, easier to handle one InputStream without blocking, or you can redirect stdout and stderr to files (null files may work on Windows definitely on Unix) so you program doesn't have to handle them.

  5. Don't forget that if your batch file reads data from stdin, you have to handle that as well. This is supported in Process and with more options in ProcessBuilder.


2

您可以尝试在cmd /c之后不使用“start”命令来启动批处理,因为“start.exe”会为batch.bat创建一个新进程。

Process p = Runtime.getRuntime().exec("cmd /c batch.bat");

这应该是适合您的正确表单。

1
正确,但如果您希望确认命令已完成,需要添加p.waitFor(); - aalku
或者你可以尝试读取p.exitValue(),如果你不想阻塞你的线程。 - jeb
如果我排除开头,整个程序就会卡住。 - Matt R. Johnson
当您启动cmd /c时,它不会显示控制台。请使用任务管理器检查进程是否正在运行。如果要显示控制台,请尝试 cmd /c start /wait cmd /c batch.bat。我成功地运行了这个测试 Process p = Runtime.getRuntime().exec("cmd /c start /wait cmd /c \"c:\\batch.bat > %temp%\\out\""); p.waitFor(); - aalku

2

根据jeb所说,或者尝试传递/wait参数给start。这将导致start等待批处理过程完成。首先在命令行中尝试使用它-比重新构建Java应用程序更快。


我同意你的观点,start /wait 应该能够工作,但是使用 /wait 开关会使得启动变得完全没有必要。 - jeb
我认为是这样,但也许楼主需要它在一个单独的窗口中吗?注意:我不是批处理文件专家。 :) - David Moles
似乎省略“start”会导致程序挂起。我不知道为什么。 - Matt R. Johnson
这是最佳方案,但仅在“start”留在其中时才有效。 - Matt R. Johnson

1

cmd /? 的输出中

/C      Carries out the command specified by string and then terminates
/K      Carries out the command specified by string but remains

因此,您需要的是:

Process p = Runtime.getRuntime().exec("cmd /k start batch.bat");

但是cmd.exe从未结束。而batch.bat在另一个进程中运行(因为它是由start.exe启动的)。 - jeb
这是错误的。"Start启动一个单独的命令提示符窗口来运行指定的程序或命令。如果没有参数使用,start会打开第二个命令提示符窗口。"在这种情况下,您不应该使用'start'。 - aalku
排除“start”会导致我的程序挂起。这是为什么? - Matt R. Johnson
批处理可能永远无法完成?当您使用与传递到“exec()”中的相同参数/标志在命令行上运行批处理文件时会发生什么? - David Moles
批处理文件单独运行良好,所以我不太确定。 - Matt R. Johnson

0
如果您只想简单地查找批处理文件进程是否已完成,为什么不添加...
echo DONE

在批处理文件的末尾?

或者如果您的程序是公共使用的,可以使用类似于...

echo finished>log.txt

首先确保它能够正常工作,然后从你的Java程序中验证它是否已完成...

if (new BufferedReader(new FileReader("log.txt")).readLine().equals("finished"))
{
    System.out.println("the batch process has finished");
}

好的,这个想法是为了检查批处理文件是否完成,以便在cmd.exe进程终止之前运行另一个命令。 - Matt R. Johnson
此外,批处理文件的内容是用户定义的。本质上,我正在制作一个批处理文件编辑器(带有一些特殊功能)。 - Matt R. Johnson

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接