无法从 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的主要内容,如果未能解决你的问题,请参考以下文章