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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/3643939/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-14 03:06:16  来源:igfitidea点击:

Java Process with Input/Output Stream

javastream

提问by James moore

I have the following code example below. Whereby you can enter a command to the bash shell i.e. echo testand have the result echo'd back. However, after the first read. Other output streams don't work?

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

Why is this or am I doing something wrong? My end goal is to created a Threaded scheduled task that executes a command periodically to /bash so the OutputStreamand InputStreamwould have to work in tandem and not stop working. I have also been experiencing the error java.io.IOException: Broken pipeany ideas?

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

Thanks.

谢谢。

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

采纳答案by Luke Woodward

Firstly, I would recommend replacing the line

首先,我建议更换线路

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

with the lines

用线条

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

ProcessBuilder is new in Java 5 and makes running external processes easier. In my opinion, its most significant improvement over Runtime.getRuntime().exec()is that it allows you to redirect the standard error of the child process into its standard output. This means you only have one InputStreamto read from. Before this, you needed to have two separate Threads, one reading from stdoutand one reading from stderr, to avoid the standard error buffer filling while the standard output buffer was empty (causing the child process to hang), or vice versa.

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

Next, the loops (of which you have two)

接下来,循环(其中有两个)

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

only exit when the reader, which reads from the process's standard output, returns end-of-file. This only happens when the bashprocess exits. It will not return end-of-file if there happens at present to be no more output from the process. Instead, it will wait for the next line of output from the process and not return until it has this next line.

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

Since you're sending two lines of input to the process before reaching this loop, the first of these two loops will hang if the process hasn't exited after these two lines of input. It will sit there waiting for another line to be read, but there will never be another line for it to read.

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

I compiled your source code (I'm on Windows at the moment, so I replaced /bin/bashwith cmd.exe, but the principles should be the same), and I found that:

我编译你的源代码(我在Windows上的那一刻,让我换成/bin/bashcmd.exe,但原则应该是相同的),我发现:

  • after typing in two lines, the output from the first two commands appears, but then the program hangs,
  • if I type in, say, echo test, and then exit, the program makes it out of the first loop since the cmd.exeprocess has exited. The program then asks for another line of input (which gets ignored), skips straight over the second loop since the child process has already exited, and then exits itself.
  • if I type in exitand then echo test, I get an IOException complaining about a pipe being closed. This is to be expected - the first line of input caused the process to exit, and there's nowhere to send the second line.
  • 输入两行后,出现前两个命令的输出,但随后程序挂起,
  • 例如,如果我输入 ,echo test然后exit,程序将退出第一个循环,因为cmd.exe进程已退出。然后程序要求输入另一行(被忽略),直接跳过第二个循环,因为子进程已经退出,然后退出自己。
  • 如果我输入exit然后echo test,我会收到一个 IOException 抱怨管道被关闭。这是意料之中的 - 输入的第一行导致进程退出,并且无处可发送第二行。

I have seen a trick that does something similar to what you seem to want, in a program I used to work on. This program kept around a number of shells, ran commands in them and read the output from these commands. The trick used was to always write out a 'magic' line that marks the end of the shell command's output, and use that to determine when the output from the command sent to the shell had finished.

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

I took your code and I replaced everything after the line that assigns to writerwith the following loop:

我拿了你的代码,并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;
    }
}

After doing this, I could reliably run a few commands and have the output from each come back to me individually.

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

The two echo --EOF--commands in the line sent to the shell are there to ensure that output from the command is terminated with --EOF--even in the result of an error from the command.

echo --EOF--发送到 shell 的行中的两个命令用于确保命令的输出--EOF--即使在命令出错的情况下也能终止。

Of course, this approach has its limitations. These limitations include:

当然,这种方法有其局限性。这些限制包括:

  • if I enter a command that waits for user input (e.g. another shell), the program appears to hang,
  • it assumes that each process run by the shell ends its output with a newline,
  • it gets a bit confused if the command being run by the shell happens to write out a line --EOF--.
  • bashreports a syntax error and exits if you enter some text with an unmatched ).
  • 如果我输入一个等待用户输入的命令(例如另一个 shell),程序似乎挂起,
  • 它假设 shell 运行的每个进程都以换行符结束其输出,
  • 如果 shell 运行的命令碰巧写出一行,它会有点困惑--EOF--
  • bash如果您输入一些带有不匹配).

These points might not matter to you if whatever it is you're thinking of running as a scheduled task is going to be restricted to a command or a small set of commands which will never behave in such pathological ways.

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

EDIT: improve exit handling and other minor changes following running this on Linux.

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

回答by gpeche

You have writer.close();in your code. So bash receives EOF on its stdinand exits. Then you get Broken pipewhen trying to read from the stdoutof the defunct bash.

你有writer.close();你的代码。因此 bash 收到 EOFstdin并退出。然后您Broken pipe在尝试从stdout已失效的 bash 中读取时得到。

回答by Shashank Jain

I think you can use thread like demon-thread for reading your input and your output reader will already be in while loop in main thread so you can read and write at same time.You can modify your program like this:

我认为你可以使用像恶魔线程这样的线程来读取你的输入,你的输出阅读器已经在主线程的 while 循环中,所以你可以同时读写。你可以像这样修改你的程序:

Thread T=new Thread(new Runnable() {

    @Override
    public void run() {
        while(true)
        {
            String input = scan.nextLine();
            input += "\n";
            try {
                writer.write(input);
                writer.flush();
            } catch (IOException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }

        }

    }
} );
T.start();

and you can reader will be same as above i.e.

你可以读到和上面一样,即

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

make your writer as final otherwise it wont be able to accessible by inner class.

使您的 writer 为 final,否则内部类将无法访问它。