Java 如何在Windows和JDK 6u45上通过管道将进程输出到文件

Java 如何在Windows和JDK 6u45上通过管道将进程输出到文件,java,processbuilder,jdk1.6,Java,Processbuilder,Jdk1.6,我有以下windows批处理文件(run.bat): 以及以下java代码,它运行批处理文件并将输出重定向到文件: public static void main(String[] args) throws IOException { System.out.println("Current java version is: " + System.getProperty("java.version")); ProcessBuilder pb = new P

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

以及以下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之前(包括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
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
脚本输出被写入文件。 从JDK 6u45和7开始,我得到以下输出:

Current java version is: 1.6.0_29
Command is: [cmd.exe, /c, run.bat, >>, stdout.txt, 2>>, stderr.txt]
Exit value: 0
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()中所做的更改有关,也可能与此无关,如所述:

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

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

ProcessBuilder pb = new ProcessBuilder("cmd.exe", "/c",
"run.bat", "Some Input With Spaces", 
">>", "stdout.txt","2>>", "stderr.txt");
在进程上使用
getOutputStream()
,而不是使用
System.out.println()
。有时,Java实现之间的语义会发生变化


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

这里有几点建议:

  • 是否需要将带空格的输入视为单个字符串(带空格),或在实际的多个输入中对其进行标识?如果第一个选项是这种情况,我建议在windows运行时引用它:

  • 与其使用shell将输入重定向到stdout.txt和stderr.txt,为什么不使用getOutputStream()和getErrorStream()使用Java呢?下面是一个使用Guava的IO包的示例。当然,您可能希望将它们放在单独的线程中,您需要适当的异常处理,等等

  • 另一个选项,为什么不创建一个
    run.bat
    包装器来进行重定向呢


这是我在


\c
右侧的所有命令行参数合并为一个参数是否有帮助?只有在传递给脚本的参数中没有空格时,这才有效。另请参见执行的进程可能会长期运行。如果我在Java进程中读取了进程输出,它就死了,进程输出将被缓冲,最终外部进程将停止。Java进程不应该以任何方式干扰外部进程。这就是为什么到文件的管道应该由操作系统来完成的原因。关于输入中的空格,任何选项都是可能的。命令行使用列表进行配置,允许传递任何组合。对于6u45之前的JDK,ProcessBuilder实现是这样工作的,没有任何问题。创建包装器脚本是一个选项,但脚本需要与外部脚本位于同一目录中(以防脚本中有其他文件和目录引用)。这是“脏的”-我想以尽可能不引人注目的方式运行代码,添加生成的文件可能会有问题。对于JDK 6,使用包装器脚本似乎是以可移植方式执行此操作的唯一方法,因此请将此答案标记为正确。@Barak您写道,手头的流程运行时间很长。将其注册为windows服务有意义吗?redirectOutput方法仅在Java 7中可用,这也需要在Java 6上工作。该链接不再工作
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();
@echo off
cmd.exe /c run.bat "%1" >> "%2" 2>> "%3"
File output = new File("C:/PBExample/ProcessLog.txt");
ProcessBuilder pb = new ProcessBuilder("cmd");
pb.redirectOutput(output);