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

发布于 2024-11-16 21:03:13 字数 285 浏览 0 评论 0原文

我读到的所有内容都表明,从 java 程序中调用批处理文件的唯一方法是执行以下操作:

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

据我了解,这会创建一个运行 CMD.exe 的进程,而 CMD.exe 又会创建一个运行批处理文件的进程。但是,CMD.exe 进程在实例化批处理文件进程后似乎会退出。

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

Everything I read says that the only way to call a batch file from within a java program is to do something like this:

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

From what I understand this creates a process to run CMD.exe, which in turn creates a process to run the batch file. However, the CMD.exe process appears to exit once it has instantiated the batch file process.

How can I confirm that the batch file has completed before the CMD process exits?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

别念他 2024-11-23 21:03:13

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

要等待批处理文件完成(退出),请删除 /c 后面的“start”。
/c 告诉 cmd.exe 在 cmd 完成时返回,这是您想要的,但是,“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 ();

现在您有了一个 Process 对象,您有多种方法来等待批处理文件完成。

最简单的方法是 .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 个字符,也可能更少),您的程序需要以某种方式处理这些数据,否则,管道您的程序和批处理过程之间的(s)将填满,并且批处理文件将挂起等待管道中的空间来添加新字符,并且批处理文件将永远不会返回。

这就是这次最初让我来到 Stack Overflow 的问题。我的批处理文件中有 17,000 多个命令,每个命令都会生成 300 多个字符到标准输出,执行命令的任何错误都会生成 1000 多个字符。

解决此问题的一些方法:

  1. @echo off 作为批处理文件中的第一行,这样 cmd 进程将不会回显批处理文件中的每个命令。如果您的批处理文件没有生成任何其他输出到 stdout 或 stderr,那么您就完成了。

  2. 如果批处理文件中的一个或多个命令可以或可能生成大量 stdout 和/或 stderr 输出,那么您必须自己处理该数据。

  3. 如果您使用“Runtime.getRuntime().exec”,您只有一种方法来处理这个问题,那就是通过调用获取批处理文件的 stdout 和 stderr 的 InputStreams:

    InputStream outIS = p.getInputStream(); // 这获取批处理文件的标准输出
        InputStream errIS = p.getErrorStream(); // 这获取批处理文件的 stderr
    

    现在您必须读取每个输入流(仅当它们有数据时才读取,否则您将等待直到读取)。假设您从其中一个输入流获得的数据比另一个输入流多得多,那么您几乎可以肯定会挂起批处理文件。有一些方法可以在不挂起的情况下读取多个流,但是,它们超出了我已经过长的答案的范围。

  4. 如果您正在使用ProcessBuilder pb= new ProcessBuilder("cmd", "/c", "batch.bat");(这是这样做的主要原因)您有一个您可以使用更多选项,因为您可以要求将 stdout 和 stderr 组合起来 pb.redirectErrorStream (true);,更容易处理一个 InputStream 而不会阻塞,或者您可以将 stdout 和 stderr 重定向到文件 (空文件可能有效在 Windows 上肯定是在 Unix 上),因此您的程序不必处理它们。

  5. 不要忘记,如果您的批处理文件从 stdin 读取数据,您也必须处理它。 Process 中支持此功能,ProcessBuilder 中提供更多选项。

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

To wait for the batch file to finish (exit) you remove the "start" after the /c.
The /c tells cmd.exe to return when cmd is finished, this you want, however, "start" means start a new cmd.exe process to execute the batch file. At this point the cmd.exe you started is finished and exits. Probably not what you want.

Either of the two code snippets below will get the batch file running and get you a Process object that you will need.

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

Or

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

Now that you have a Process object you have multiple ways to wait for your batch file to finish.

The simplest way is .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 ();
}

You could also use waitFor(long timeout, TimeUnit unit) or p.isAlive() if they meet your needs better.

Warning If your batch is going to output a lot of data (probably more than 1024 characters, maybe less) to stdout and / or stderr your program needs to handle this data in some manner, otherwise, the pipe (s) between your program and the batch process will fill up and the batch file will hang waiting for room in the pipe to add new characters and the batch file will never return.

This is the problem that originally brought me to Stack Overflow this time. I had 17,000+ commands in a batch file and each command generated 300+ characters to stdout and any errors executing a command generated 1000+ characters.

Some of the ways to solve this:

  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.

葬心 2024-11-23 21:03:13

jeb 所说的,或者尝试将 /wait 参数传递给 start。这应该会导致 start 等待批处理完成。首先在命令行尝试一下——比重建 Java 应用程序更快。

What jeb said, or try passing the /wait parameter to start. That should cause start to wait till the batch process completes. Try it at the command line first -- faster than rebuilding your Java app.

等待我真够勒 2024-11-23 21:03:13

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

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

这应该是适合您的正确形式。

You could try to start the batch without the "start" command after cmd /c, as the "start.exe" creates a new process for the batch.bat.

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

This should be the correct form for you.

回眸一笑 2024-11-23 21:03:13

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");

From the output of cmd /?

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

Thus what you need is:

Process p = Runtime.getRuntime().exec("cmd /k start batch.bat");
空城旧梦 2024-11-23 21:03:13

如果您想简单地了解批处理文件处理是否完成,为什么不在

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");
}

If you want to simply find out whether the batch file process finished or not, why not add...

echo DONE

at the end of the batch file?

Or if you program is for public use, something like...

echo finished>log.txt

would work. Then verify that it is finished from within your java program...

if (new BufferedReader(new FileReader("log.txt")).readLine().equals("finished"))
{
    System.out.println("the batch process has finished");
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文