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

时间:2010-07-01 16:53:07

标签: java process inputstream

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

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

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

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

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

6 个答案:

答案 0 :(得分:11)

我意识到这个问题已经过时了,但我刚遇到同样的问题。为了解决这个问题,我使用了这段代码..

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

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

<强>更新

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

手动合并

答案 1 :(得分:6)

您需要在线程中执行此操作。例如,记录标准输出:

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

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

答案 2 :(得分:6)

我有一个C程序打印到stdout的问题......

C代码中带有redirectErrorStream(true)的{​​{1}}解决方案为我做了诀窍。

答案 3 :(得分:4)

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

诀窍是在使用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;
    }
}

答案 4 :(得分:2)

我遇到了与ftp相同的问题,直到我注意到ftp检测到它是否是从终端调用的。

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

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

答案 5 :(得分:1)

我遇到了完全相同的问题。不幸的是

processBuilder.redirectErrorStream(true); 

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

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

它帮助我找出了运行每个线程的终端命令出了什么问题。