2012-01-05 4 views
1

Javaで外部プロセスの出力/エラーストリームを処理する標準的な方法は、2つの余分なスレッドを使用して、プロセスがブロックされないように、出力ストリームとエラーストリームが生成されます。余分なスレッドを使わずにJavaでプロセスの出力/エラーを個別に処理する

次に、次はどうなりますか?

public static void main(String[] args) throws IOException, InterruptedException { 
    ProcessBuilder processBuilder = new ProcessBuilder(args); 
    Process process = processBuilder.start(); 

    InputStream outputStream = null, errorStream = null; 
    ByteArrayOutputStream outputBuffer = new ByteArrayOutputStream(); 
    ByteArrayOutputStream errorBuffer = new ByteArrayOutputStream(); 
    try { 
    outputStream = process.getInputStream(); 
    errorStream = process.getErrorStream(); 

    byte[] tmp = new byte[1024]; 

    while (true) { 
     int outputBytes = readAvailablOnce(outputStream, outputBuffer, tmp); 
     int errorBytes = readAvailablOnce(errorStream, errorBuffer, tmp); 
     if (outputBytes == 0 && errorBytes == 0) { 
     try { 
      process.exitValue(); 
      break; 
     } catch (IllegalThreadStateException e) { 
      // keep on looping 
     } 
     } 
    } 
    readAvailableAll(outputStream, outputBuffer, tmp); 
    readAvailableAll(errorStream, errorBuffer, tmp); 

    } finally { 
    closeQuietly(outputStream); 
    closeQuietly(errorStream); 
    } 

    System.out.println(outputBuffer.toString("ASCII")); 
    System.err.println(errorBuffer.toString("ASCII")); 
    System.err.println("exit code: " + process.exitValue()); 
} 

private static void closeQuietly(InputStream in) { 
    if (in != null) { 
    try { 
     in.close(); 
    } catch (IOException e) { 
     // ignored 
    } 
    } 
} 

private static int readAvailablOnce(
    InputStream inputStream, OutputStream outputStream, byte[] buffer) 
throws IOException { 
    int bytesRead = 0; 
    if (inputStream.available() > 0) { 
    bytesRead = inputStream.read(buffer); 
    outputStream.write(buffer, 0, bytesRead); 
    } 
    return bytesRead; 
} 

private static void readAvailableAll(
    InputStream inputStream, OutputStream outputStream, byte[] buffer) 
throws IOException { 
    if (inputStream.available() > 0) { 
    int bytesRead = 0; 
    while ((bytesRead = inputStream.read(buffer)) >= 0) { 
     outputStream.write(buffer, 0, bytesRead); 
    } 
    } 
} 

これは、私が試したいくつかの例(「dir」、「ps aux」など)で実際にうまく動作します。

また、BuffersやCharsetDecodersで多かれ少なかれ複雑な作業をやり始めない限り、出力を行単位で簡単に扱うことができないという欠点があります。

しかし、出力が許容できないほど大きいものはありません(ただし、使用する前に出力全体をバッファリングする必要はありません)。

私は1.5と1.6のJVM(Windows XPとLinux)のみを試しました。

また、このコードは、プロセスの最終出力ビットが読み込み(InputStream.available()> 0)にすぐに利用できることを前提にしています。

誰でも、このコードに何が(または何か)間違っている(または良いアイデアがある)のは知っているでしょうか?

+0

あなたは[codereview](http://codereview.stackexchange.com/)について知っていますか?ベータ版ですが、うまく機能しているようです。 –

+0

私はしなかった!ありがとう。私は2つの余分なスレッドのアプローチは古典的な答えであり、それは正しく感じていないので、一般的なソリューションが実際にJavaで動作するかどうか(私の小さなテストを超えて)に興味があります。私は両方のツールでそれを持っていることが無礼でないなら、 "codereview"で提出するかもしれません。 – viphe

答えて

1

ProcessBuilder.redirectErrorStream(true)を使用すると、現在のスレッドで1つのストリームを読み取ることができます。バックグラウンドスレッドは必要ありません。

+0

標準とエラー出力のマージに気にしない場合は、あなたが提案するものが確かに行く方法です。私の説明を修正する... – viphe

関連する問題