【问题标题】:How do I Pipe process output to a file on Windows and JDK 6u45如何将进程输出通过管道传输到 Windows 和 JDK 6u45 上的文件
【发布时间】:2013-06-10 00:06:10
【问题描述】:

我有以下 Windows 批处理文件 (run.bat):

@echo off
echo hello batch file to sysout

还有以下 java 代码,它运行批处理文件并将输出重定向到文件:

public static void main(String[] args) throws IOException {
    System.out.println("Current java version is: " + System.getProperty("java.version"));

    ProcessBuilder pb =
            new ProcessBuilder("cmd.exe", "/c",
                    "run.bat"
                     ,">>", "stdout.txt","2>>", "stderr.txt"
                    );
    System.out.println("Command is: " + pb.command());

    Process proc = pb.start();

    InputStream in = proc.getInputStream();
    BufferedReader reader = new BufferedReader(new InputStreamReader(in));

    String line = null;
    while ((line = reader.readLine()) != null) {
        System.out.println(line);
    }

    int exitValue = proc.exitValue();
    System.out.println("Exit value: " + exitValue);
}

在 JDK6u43 及以下的 JDK 上,我得到以下输出:

Current java version is: 1.6.0_29
Command is: [cmd.exe, /c, run.bat, >>, stdout.txt, 2>>, stderr.txt]
Exit value: 0

并且脚本输出被写入文件。 从 JDK 6u45 和 7 开始,我得到以下输出:

Current java version is: 1.6.0_45
Command is: [cmd.exe, /c, run.bat, >>, stdout.txt, 2>>, stderr.txt]
hello batch file to sysout
Exit value: 0

并且没有任何内容写入输出文件。

这可能与 Runtime.exec() 中所做的更改相关,也可能不相关,描述于:http://www.oracle.com/technetwork/java/javase/6u45-relnotes-1932876.html

在 Windows 上启动进程并将输出重定向到文件的正确方法是什么?

注意:在现实场景中,要执行的命令可能包含带空格的参数,如:

ProcessBuilder pb = new ProcessBuilder("cmd.exe", "/c",
"run.bat", "Some Input With Spaces", 
">>", "stdout.txt","2>>", "stderr.txt");

【问题讨论】:

标签: java processbuilder jdk1.6


【解决方案1】:

这是我在http://tamanmohamed.blogspot.in/2012/06/jdk7-processbuilder-and-how-redirecting.html找到的最简单的方法

File output = new File("C:/PBExample/ProcessLog.txt");
ProcessBuilder pb = new ProcessBuilder("cmd");
pb.redirectOutput(output);

【讨论】:

  • redirectOutput 方法仅适用于 Java 7,这也需要在 Java 6 上工作。
【解决方案2】:

这里有几个建议:

  • 是否需要将带空格的输入视为单个字符串(带空格),或者在实际的多个输入中进行处理?如果是第一个选项,我建议在 Windows 运行时引用它:

ProcessBuilder pb = new ProcessBuilder("cmd.exe", "/c", 
"run.bat", "\"Some Input With Spaces\"", 
">>", "stdout.txt","2>>", "stderr.txt");
  • 与其使用shell 将输入重定向到stdout.txt 和stderr.txt,为什么不使用Java 使用getOutputStream() 和getErrorStream() 来实现呢?这是使用 Guava 的 IO 包的示例。当然,您可能希望将它们放在单独的线程中,您需要适当的异常处理等。

InputStream stdout = new BufferedInputStream(proc.getInputStream());
FileOutputStream stdoutFile = new FileOutputStream("stdout.txt");
ByteStreams.copy(stdout, stdoutFile);

InputStream stderr = new BufferedInputStream(proc.getErrorStream());
FileOutputStream stderrFile = new FileOutputStream("stderr.txt");
ByteStreams.copy(stderr, stderrFile);

stdout.close();
stderr.close();
stdoutFile.close();
stderrFile.close();
  • 另一种选择,为什么不创建一个 run.bat 包装器来进行重定向?

@echo off
cmd.exe /c run.bat "%1" >> "%2" 2>> "%3"

【讨论】:

  • 执行的进程可能运行时间长。如果我在 Java 进程中读取进程输出并且它死了,进程输出将被缓冲,最终外部进程将停止。 Java 进程不应以任何方式干扰外部进程。这就是为什么管道传输到文件应该由操作系统以某种方式完成。
  • 关于输入中的空格,任何选项都是可能的。命令行使用 List 进行配置,允许传递任何组合。在 6u45 之前的 JDK 中,ProcessBuilder 实现可以这样工作,没有任何问题。
  • 创建包装脚本是一种选择,但该脚本需要与外部脚本位于同一目录中(以防脚本中有其他文件和目录引用)。这很“脏”——我想以尽可能不显眼的方式运行代码,添加生成的文件可能会出现问题。
  • 使用包装脚本似乎是 JDK 6 以可移植方式执行此操作的唯一方法,因此将此答案标记为正确。
  • @Barak 你写道,手头的进程正在运行很长时间。将其注册为 Windows 服务是否有意义?
【解决方案3】:

在进程上使用getOutputStream(),而不是使用System.out.println()。有时,Java 实现之间的语义会发生变化。

这实际上似乎是一个错误修复 - 较新的实现是有意义的。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2019-01-24
    • 1970-01-01
    • 1970-01-01
    • 2016-04-18
    • 2010-10-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多