web-dev-qa-db-ja.com

Javaプロセス(Runtime.getRuntime()。exec()またはProcessBuilder))からInputStreamを読み取れません

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

これは古い質問だと思いますが、同じ問題を経験しました。これを修正するために、私はこのコードを使用しました。

_ 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)で手動でマージする必要があるだけかもしれません

13

標準出力に出力するCプログラムでこの問題が発生しました...

CコードでredirectErrorStream(true)を使用したfflush(stdout)ソリューションが私にとってはうまくいきました。

6
Pierre Brunetti

この作業はスレッドで行う必要があります。たとえば、標準出力をログに記録するには:

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

次に、入力処理のために2番目のスレッドが必要です。そして、あなたはstdoutと同じようにstderrを扱いたいかもしれません。

6

数日間苦労し、多くのオプションを試した後解決策を見つけました。 Ubuntu 14.04 x64でjdk 8 u 25を使用しています。 このコードレビューの投稿 からヒントが得られました。

コツは、BufferedReaderで何かを読み取る前にInputStream#available()を使用することです。私は個人的に2つのスレッドを持っています。1つはstdout用で、もう1つは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;
    }
}
3
Jules Randolph

Ftpが端末から呼び出されたかどうかをftpが検出することに気づくまで、私はftpで同じ問題を抱えていました。

端末からftpが呼び出されない場合、冗長(-v)オプションが指定されていない限り、標準出力への出力は中断されます。

ストリームがブロックされるのは、何も書き込まれないためです。

2
Clausen

私はまったく同じ問題を抱えていました。残念ながら

processBuilder.redirectErrorStream(true); 

うまくいきませんでした。でも、何が悪いのかが分かりました。次のコード行を使用して、stderrの内容を表示してみてください。

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

これは、各スレッドで実行されている端末コマンドの何が問題であるかを理解するのに役立ちました。

1
Zahra