带有输入/输出流的 Java 进程

标签 java stream

我在下面有以下代码示例。从而您可以向 bash shell 输入命令,即 echo test并让结果回显。然而,在第一次阅读之后。其他输出流不起作用?

为什么会这样或者我做错了什么?我的最终目标是创建一个 Threaded 计划任务,该任务定期向/bash 执行命令,因此 OutputStreamInputStream必须协同工作,而不是停止工作。我也遇到了错误 java.io.IOException: Broken pipe有什么想法吗?

谢谢。

String line;
Scanner scan = new Scanner(System.in);

Process process = Runtime.getRuntime ().exec ("/bin/bash");
OutputStream stdin = process.getOutputStream ();
InputStream stderr = process.getErrorStream ();
InputStream stdout = process.getInputStream ();

BufferedReader reader = new BufferedReader (new InputStreamReader(stdout));
BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(stdin));

String input = scan.nextLine();
input += "\n";
writer.write(input);
writer.flush();

input = scan.nextLine();
input += "\n";
writer.write(input);
writer.flush();

while ((line = reader.readLine ()) != null) {
System.out.println ("Stdout: " + line);
}

input = scan.nextLine();
input += "\n";
writer.write(input);
writer.close();

while ((line = reader.readLine ()) != null) {
System.out.println ("Stdout: " + line);
}

最佳答案

首先,我建议更换线路

Process process = Runtime.getRuntime ().exec ("/bin/bash");

用线条
ProcessBuilder builder = new ProcessBuilder("/bin/bash");
builder.redirectErrorStream(true);
Process process = builder.start();

ProcessBuilder 是 Java 5 中的新功能,可以更轻松地运行外部进程。在我看来,它比 Runtime.getRuntime().exec() 的改进最显着。是它允许您将子进程的标准错误重定向到其标准输出。这意味着您只有一个 InputStream从中读取。在此之前,您需要有两个单独的线程,其中一个来自 stdout和一篇来自 stderr 的读物, 以避免在标准输出缓冲区为空时填充标准错误缓冲区(导致子进程挂起),反之亦然。

接下来,循环(其中有两个)
while ((line = reader.readLine ()) != null) {
    System.out.println ("Stdout: " + line);
}

仅在 reader 时退出,从进程的标准输出中读取,返回文件结尾。这仅在 bash 时发生进程退出。如果当前进程没有更多输出,它不会返回文件结尾。相反,它将等待进程的下一行输出,并且直到它有下一行才返回。

由于您在到达此循环之前向进程发送两行输入,因此如果在这两行输入之后进程尚未退出,则这两个循环中的第一个将挂起。它将坐在那里等待读取另一行,但永远不会有另一行读取。

我编译了你的源代码(我现在在 Windows 上,所以我用 /bin/bash 替换了 cmd.exe ,但原理应该是一样的),我发现:
  • 输入两行后,出现前两条命令的输出,但随后程序挂起,
  • 如果我输入,比如说,echo test ,然后 exit ,程序使其退出自 cmd.exe 以来的第一个循环进程已经退出。然后程序要求输入另一行(被忽略),直接跳过第二个循环,因为子进程已经退出,然后退出自己。
  • 如果我输入 exit然后 echo test ,我收到一个 IOException 提示管道被关闭。这是意料之中的 - 输入的第一行导致进程退出,并且无处可发送第二行。

  • 我曾在我曾经开发的一个程序中看到过一个与您似乎想要的类似的技巧。该程序保留了许多 shell,在其中运行命令并读取这些命令的输出。所使用的技巧是始终写出一个标记 shell 命令输出结束的“魔术”行,并使用它来确定发送到 shell 的命令的输出何时完成。

    我拿走了你的代码,并替换了分配给 writer 的行之后的所有内容。使用以下循环:
    while (scan.hasNext()) {
        String input = scan.nextLine();
        if (input.trim().equals("exit")) {
            // Putting 'exit' amongst the echo --EOF--s below doesn't work.
            writer.write("exit\n");
        } else {
            writer.write("((" + input + ") && echo --EOF--) || echo --EOF--\n");
        }
        writer.flush();
    
        line = reader.readLine();
        while (line != null && ! line.trim().equals("--EOF--")) {
            System.out.println ("Stdout: " + line);
            line = reader.readLine();
        }
        if (line == null) {
            break;
        }
    }
    

    这样做之后,我可以可靠地运行一些命令,并让每个命令的输出单独返回给我。

    两人echo --EOF--发送到 shell 的行中的命令用于确保命令的输出以 --EOF-- 终止。即使是命令出错的结果。

    当然,这种方法有其局限性。这些限制包括:
  • 如果我输入一个等待用户输入的命令(例如另一个 shell),程序似乎挂起,
  • 它假设 shell 运行的每个进程都以换行符结束其输出,
  • 如果 shell 运行的命令碰巧写出一行 --EOF--,它会有点困惑。 .
  • bash如果您输入一些不匹配的文本,则报告语法错误并退出 ) .

  • 如果您想作为计划任务运行的任何内容将被限制为一个命令或一小组永远不会以这种病态方式运行的命令,那么这些点对您来说可能无关紧要。

    编辑 :在 Linux 上运行后改进退出处理和其他小的更改。

    关于带有输入/输出流的 Java 进程,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/3643939/

    相关文章:

    azure - SendGrid:如何从 Azure Blob 存储附加文件?

    java - 您在 Log4j 中见过基于 NDC 将日志记录到单独文件的附加程序吗?

    java - 可运行对象与方法引用和垃圾

    c# - 从流 .NET 读取时性能低下

    c++ - HTTP 流服务器 : threads?

    flutter - 流监听的竞争条件

    java - 在 Twilio 发起的通话期间发送短信或调用其他人

    java - 模拟一个类(class)的私有(private)领域

    java - Spring MVC - 无法渲染 View

    c# - C# 从视频流中获取图像