无法从 Java 进程(Runtime.getRuntime().exec() 或 ProcessBuilder)读取 InputStream

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/3159913/
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-13 16:56:52  来源:igfitidea点击:

Unable to read InputStream from Java Process (Runtime.getRuntime().exec() or ProcessBuilder)

javaprocessinputstream

提问by msg

I'm trying to start a process externally with Java and can't read anything from its InputStream.

我正在尝试使用 Java 从外部启动一个进程,但无法从其 InputStream 中读取任何内容。

If I'm starting a process with commands like "ls", "ps" or "kill" everything works fine. I can start the process and get information either on the InputStream or the ErrorStream of the Process.

如果我使用“ls”、“ps”或“kill”等命令启动进程,一切正常。我可以启动流程并获取有关流程的 InputStream 或 ErrorStream 的信息。

If I try to use a command like "ftp" or "telnet" both InputStream and ErrorStream are blocking my program when trying to read. No information is passed through these streams at any time.

如果我尝试使用诸如“ftp”或“telnet”之类的命令,则 InputStream 和 ErrorStream 在尝试读取时都会阻塞我的程序。任何时候都不会通过这些流传递任何信息。

Can anyone explain the behavior? Is it just not possible with these commands or do I have an issue with my implementation?

任何人都可以解释这种行为吗?这些命令是不可能的,还是我的实现有问题?

     String processName = _configuration.getProgramCommand().getCommand();
   ProcessBuilder procBuilder = new ProcessBuilder(processName);   

   System.out.println("Starting process "+processName);   
   _proc = Runtime.getRuntime().exec(processName);// procBuilder.start();            

   if(!procBuilder.redirectErrorStream()) {    
    _errorWorker = new ProcessErrorWorker(_proc);
    _errorWorker.start();   
   }

   String proc_start_answer = _configuration.getNextCommand().getCommand();
   System.out.println("Waiting for process answer '"+proc_start_answer+"'");
   BufferedReader input = new BufferedReader(new InputStreamReader(_proc.getInputStream()));      

   String answer = "";  

   try {         
    System.out.println("inputstream ready: "+input.ready());
    answer+=input.readLine(); 
    System.out.println("process answer:  "+answer);
    input.close();        

   } catch(Exception e) {
    System.out.print(e.getMessage());     
   } 

回答by Hendrik Brummermann

You need to do this work in a Thread. For example to log the standard output:

您需要在 Thread 中完成这项工作。例如记录标准输出:

Process process = Runtime.getRuntime().exec(command);
LogStreamReader lsr = new LogStreamReader(process.getInputStream());
Thread thread = new Thread(lsr, "LogStreamReader");
thread.start();


public class LogStreamReader implements Runnable {

    private BufferedReader reader;

    public LogStreamReader(InputStream is) {
        this.reader = new BufferedReader(new InputStreamReader(is));
    }

    public void run() {
        try {
            String line = reader.readLine();
            while (line != null) {
                System.out.println(line);
                line = reader.readLine();
            }
            reader.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

Then you need a second thread for input handling. And you might want to deal with stderr just like stdout.

然后你需要第二个线程来处理输入。您可能想像处理 stdout 一样处理 stderr。

回答by Andrew T Finnell

I realize this is question is old, but I just experienced the same problem. In order to fix this I used this code..

我意识到这是一个老问题,但我刚刚遇到了同样的问题。为了解决这个问题,我使用了这段代码..

 List<String> commandAndParameters = ...;
 File dir = ...; // CWD for process

 ProcessBuilder builder = new ProcessBuilder();
 builder.redirectErrorStream(true); // This is the important part
 builder.command(commandAndParameters);
 builder.directory(dir);

 Process process = builder.start();

 InputStream is = process.getInputStream();

It appears the process is expecting you to also read from the Error stream. The best fix to this is to merge the input and error stream together.

该过程似乎希望您也从错误流中读取。对此的最佳解决方法是将输入和错误流合并在一起。

Update

更新

I didn't see that you attempted to read from the error stream also. It could just be that you need to merge them manually with redirectErrorStream(true)

我没有看到您也尝试从错误流中读取数据。可能只是您需要手动将它们与redirectErrorStream(true)

回答by Pierre Brunetti

I had this issue with a C program printing to stdout...

我在将 C 程序打印到标准输出时遇到了这个问题...

The redirectErrorStream(true)solution with a fflush(stdout)in the C code did the trick for me.

C 代码中redirectErrorStream(true)带有 a的解决方案fflush(stdout)对我有用。

回答by Zahra

I had the exact same problem. Unfortunately

我有同样的问题。很遗憾

processBuilder.redirectErrorStream(true); 

didn't work for me; it gave me an idea of what is wrong though. Try using following line of code to display stderr contents:

对我不起作用;它让我知道出了什么问题。尝试使用以下代码行显示 stderr 内容:

BufferedReader err=
             new BufferedReader(new InputStreamReader(process.getErrorStream())); 

It helped me figure out what was wrong with my terminal commands running through each thread.

它帮助我弄清楚我的终端命令在每个线程中运行时出了什么问题。

回答by Clausen

I had the same problem with ftp, until I noticed that ftp detects if it is called from a terminal or not.

我对 ftp 有同样的问题,直到我注意到 ftp 检测它是否从终端调用。

When ftp is not called from a terminal, it suspends any output to stdout, unless the verbose (-v) option is supplied.

当没有从终端调用 ftp 时,它会将任何输出挂起到 stdout,除非提供了详细 (-v) 选项。

The reason for the streams blocking is that nothing is written to them.

流阻塞的原因是没有向它们写入任何内容。

回答by Jules Randolph

After struggling for days, and trying many options I found a solution. Working on Ubuntu 14.04 x64, with jdk 8 u 25. This codereview postgave me the hint.

经过几天的努力,并尝试了很多选择,我找到了一个解决方案。使用 jdk 8 u 25 在 Ubuntu 14.04 x64 上工作。这篇 codereview 帖子给了我提示。

The trick is to use InputStream#available()before reading anything with the BufferedReader. I personally have two threads, one for stdout and the other for stderr. Here is a simple running snippet I extrapolated from my program. If you don't want to read all, just jump to readLine()

诀窍是使用InputStream#available()阅读与任何事情之前BufferedReader。我个人有两个线程,一个用于 stdout,另一个用于 stderr。这是我从我的程序中推断出来的一个简单的运行片段。如果您不想阅读所有内容,请跳至readLine()

import org.apache.commons.io.IOUtils;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.InputStream;
import java.io.BufferedReader;
import java.nio.charset.Charset;


public class LogHandler {
    private BufferedReader bufferedReader;
    private InputStream inputStream;
    private Thread thread;
    private boolean isRunning;

    public void open (InputStream inputStream) {
        this.inputStream = inputStream;
        this.bufferedReader = new BufferedReader(new InputStreamReader(inputStream, Charset.defaultCharset()));
        isRunning = true ;

        if(thread!=null){
            thread = new Thread (()->{
                while(isRunning) {
                    String line = null;
                    try {
                        line = readLine();
                    } catch (IOException e) {
                        isRunning = false;
                    }
                    if(line == null) {
                        try {
                            Thread.sleep(150);
                        } catch (InterruptedException ie) {
                            isRunning=false;
                            Thread.currentThread().interrupt();
                        }
                    } else {
                        System.out.println(line);
                    }
                }
            });
        } else throw new IllegalStateException("The logger is already running");
        thread.start();
    }

    public void close() throws InterruptedException {
        if(thread!=null){
            thread.interrupt();
            thread.join();
            thread = null;
            IOUtils.closeQuietly(inputStream);
        } else throw new IllegalStateException("The logger is not running");         }

    private String readLine() throws IOException {
        if(inputStream.available()>0) { // <--------- THIS IS IT
            int kar = bufferedReader.read();
            if (kar != -1) {
                StringBuilder buffer = new StringBuilder(30);
                while (kar != -1 && kar != (int) '\n') {
                    buffer.append((char) kar);
                    kar = bufferedReader.read();
                }
                return buffer.toString();
            }
        }
        return null;
    }
}