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

Posted

技术标签:

【中文标题】无法从 Java 进程(Runtime.getRuntime().exec() 或 ProcessBuilder)读取 InputStream【英文标题】:Unable to read InputStream from Java Process (Runtime.getRuntime().exec() or ProcessBuilder) 【发布时间】:2011-03-10 17:59:24 【问题描述】:

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

如果我使用“ls”、“ps”或“kill”等命令启动进程,一切正常。我可以启动进程并获取有关进程的 InputStream 或 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());     
    

【问题讨论】:

您解决了这个问题吗?您可以在解决方案中发布吗? 【参考方案1】:

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

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。

【讨论】:

感谢您的回答。实际上我一直在一个线程中测试它,在所有变体中。我尝试读取整行,读取单个字符和读取字节。没有效果。我无法从这个 InputStream 中得到任何信息,它肯定会响应。它仅在启动的进程终止时才有效(如“ps”、“ls”)。但我还需要它与 ftp 和 telnet 等“交互式”进程一起运行,一定有一个我不知道的问题。还有什么想法吗?【参考方案2】:

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

 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)合并

【讨论】:

但是如果他们正在获取标准输出,为什么他们将其命名为 errorStream?有什么想法吗?【参考方案3】:

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

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

【讨论】:

【参考方案4】:

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

processBuilder.redirectErrorStream(true); 

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

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

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

【讨论】:

【参考方案5】:

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

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

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

【讨论】:

【参考方案6】:

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

诀窍是在使用BufferedReader 阅读任何内容之前使用InputStream#available()。我个人有两个线程,一个用于 stdout,另一个用于 stderr。这是一个简单的运行 sn-p 我从我的程序中推断出来的。不想看完就直接跳到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;
    

【讨论】:

这并不能告诉你是否有完整的行,所以你仍然可以阻止readLine()

以上是关于无法从 Java 进程(Runtime.getRuntime().exec() 或 ProcessBuilder)读取 InputStream的主要内容,如果未能解决你的问题,请参考以下文章

从Java中的进程获取打开的窗口数量

准备Java面试?mysql无法启动1067进程意外终止

无法从本地系统帐户检索的流程信息

简单的 Java 进程输入/输出失败

为啥 Python 不能通过子进程执行 java.exe?

Jps介绍以及解决jps无法查看某个已经启动的java进程问题盗用