【问题标题】:Java: is there a way to run a system command and print the output during execution?Java:有没有办法运行系统命令并在执行期间打印输出?
【发布时间】:2013-10-06 20:00:52
【问题描述】:

我有一个 python 脚本,需要很长时间才能完成。我想从 Java 中运行它,但也在执行时输出脚本的输出,以便我可以判断它是否正常运行。

我已经搜索并只找到了在系统命令完成后而不是在执行期间输出输出的示例。

脚本运行时有什么办法吗?

这就是我所拥有的

public void doSomething() throws IOException {
    String[] callAndArgs = {"python", "/hi.py"};
    Process p = Runtime.getRuntime().exec(callAndArgs);
    BufferedReader stdInput = new BufferedReader(new InputStreamReader(p.getInputStream()));
    BufferedReader stdError = new BufferedReader(new InputStreamReader(p.getErrorStream()));

    String s;
    while ((s = stdInput.readLine()) != null) {
        System.out.println(s);
    }

    while ((s = stdError.readLine()) != null) {
        System.out.println(s);
    }
}

【问题讨论】:

  • FWIW,我已经复制了你的结果。不只是你。
  • FWIW:变量stdInput 真的应该被称为stdOutput。 :-)
  • 即使从原始 InputStream 中读取单个字符也不会给我们任何东西,直到最后。我认为这是一个 python 的东西。
  • 最后:如果我让它产生其他东西(在我的例子中,java 运行一个有暂停的 Java 程序),我会看到输出,而不是同时结尾。这是一个蟒蛇的事情。我不知道是什么,所以这不是答案,因此发布评论。

标签: java python unix stream output


【解决方案1】:

我设法让它像这样工作(注意它需要 java7):

package test;
import java.lang.ProcessBuilder.Redirect;

public class Test {

    public static void main(String... args) throws Exception {
        ProcessBuilder pb = new ProcessBuilder("python","/home/foobar/Programming/test/src/test/test.py");
        pb.redirectOutput(Redirect.INHERIT);
        Process p = pb.start();
        p.waitFor();
    }

}

python(注意我在 python 上刷新以使其使用 sys.stdout.flush())

import time,sys
c =0
while c<=50:
    time.sleep(1)
    print("----")
    c = c +1
    sys.stdout.flush()

请注意,如果您不想在循环中刷新,可以使用:

ProcessBuilder pb = new ProcessBuilder("python","-u","/home/foobar/Programming/NetBeansProjects/test/src/test/test.py");

重定向.INHERIT

表示子进程 I/O 源或目标将与当前进程的相同。这是大多数操作系统命令解释器(shell)的正常行为。

【讨论】:

  • 值得注意的是,如果您在 Python 端 flush 也可以正常工作,那么您的 original 代码也可以正常工作,因此 MadProgrammer was correct 这是需要的 Python 端修复程序.
【解决方案2】:

我已经搜索并只找到了我们在之后输出输出的示例 系统命令已完成,而不是在执行期间。

这很奇怪,因为您的示例应该在命令执行时转储输出。

您可以尝试直接从InputStream 读取而不是使用BufferedReader,因为可能直到进程退出后才会满足readLine 所需的条件。

我还建议您直接使用ProcessBuilder 而不是Process,因为除此之外,它还允许您将错误流的输出重定向到输入流,只允许您读取一个流而不是两个...

这也可能是 Python 以及它如何刷新输出缓冲区的问题...

例如,与其等待BufferedReader 决定何时返回,不如尝试在流中出现/报告时打印每个字符

            ProcessBuilder pb = new ProcessBuilder("test.py");
            pb.redirectError();
            Process p = pb.start();

            InputStream is = null;
            try {
                is = p.getInputStream();
                int in = -1;
                while ((in = is.read()) != -1) {
                    System.out.print((char)in);
                }
            } finally {
                try {
                    is.close();
                } catch (Exception e) {
                }
            }

更新

稍微阅读一下,Python 似乎在将其发送到标准输出之前将其缓冲出来。我认为您无法在 Java 端解决此问题,但需要更改 Python 的运行方式或脚本的工作方式。

查看How to flush output of Python print?了解更多详情

【讨论】:

  • 我已经做过那个实验了。 InputStream 在进程终止之前不会给出任何字符。我想它一定是python的东西。
  • Python 端的 +1 flush 确实解决了问题(即使使用 OP 的原始代码)。
  • @MadProgrammer 随时在您的答案中添加我的答案中的python 代码,因为这似乎是一种更好的方法。您也可以将python -u 用于flush
【解决方案3】:

我怀疑您正在写入 stderr,因为您阻塞了 stdin,所以您看不到它。使用ProcessBuilder 而不是exec。这样,您可以将 stderr 和 stdin 重定向到单个流中。

这是一个例子:

import java.io.*;

public class Test {
    public static void main(String... args) throws IOException {

        ProcessBuilder pb =
                new ProcessBuilder("test.py");

        pb.redirectErrorStream(true);
        Process proc = pb.start();

        Reader reader = new InputStreamReader(proc.getInputStream());
        BufferedReader bf = new BufferedReader(reader);
        String s;
        while ((s = bf.readLine()) != null) {
            System.out.println(s);
        }
    }
}

或者,您可以生成线程以分别从标准输入/标准错误中读取。

要寻找的另一件事是 python 的输出缓冲。您可以通过以下方式查看是否是原因:

import sys
sys.stdout.flush()

写入标准输出后

【讨论】:

  • 是什么让您认为Runtime.exec 会阻塞直到程序终止? "...因此您可以在脚本运行时读取标准输入..." stdout(来自进程),我认为您的意思是。
  • 我认为为了阻止你必须使用 process.waitFor();不确定
  • 我编辑你的帖子做了一个 BufferedReader(reader) ,然后使用 bufferedReader.readLine() 编译
  • 我对解决方案很感兴趣,所以我复制了 ide 中的代码来运行它:)
  • 这也不起作用。与OP相同的问题。 (我写的是标准输出,而不是标准错误。)
【解决方案4】:

不要在 while 循环中使用 #readLine 作为条件。而是将您的 inputStream 包装在扫描仪中并使用 #hasNextLine()

    Scanner in = new Scanner(p.getInputStream());
    while (in.hasNextLine()) {
        System.out.println(in.nextLine());
    }

【讨论】:

  • @MadProgrammer 因为他希望它在运行时更新,而不是在运行后更新。
  • 好的,让我们开始吧,BufferedRead 中没有 hasNextLine 或 nextLine 方法,然后移到那个 readLine "读取一行文本。一行被认为是由一行中的任何一个终止feed ('\n')、回车 ('\r') 或回车后紧跟换行。"
  • 我仍然看不出这与使用 BufferedReader 有何不同?
  • 我可以确认使用Scanner 不会改变任何东西。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-01-16
  • 1970-01-01
  • 1970-01-01
  • 2013-10-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多