web-dev-qa-db-ja.com

Apache-commons execからのプロセス出力

私はここで私の知恵の終わりにいます。これは単純なものだと確信しています。おそらくJavaとストリームの理解に大きな穴があります。クラスをたくさん持っているので、ポークしようとすることに少し圧倒されます。 APIを使用して、さまざまな入出力ストリームをいつどのように使用するかを把握します。

私は、Apache commonsライブラリの存在について学びました(自己学習Java失敗))。現在、いくつかのRuntime.getRuntime()。execを変換して、commons-execを使用しようとしています。すでに6か月に1度は修正されていますが、この問題が発生し、execのスタイルの問題が解消されます。

このコードはPerlスクリプトを実行し、実行中のスクリプトのstdoutをGUIに表示します。

呼び出しコードは、swingworker内にあります。

PumpStreamHandlerの使い方が分からなくなってきています...とにかくここに古いコードがあります:

String pl_cmd = "Perl script.pl"
Process p_pl = Runtime.getRuntime().exec( pl_cmd );

BufferedReader br_pl = new BufferedReader( new InputStreamReader( p_pl.getInputStream() ) );

stdout = br_pl.readLine();
while ( stdout != null )
{
    output.displayln( stdout );
    stdout = br_pl.readLine();
}

これは私がずっと前に完全に理解していないコピー貼り付けコードで得られるものだと思います。上記で私が想定しているのはプロセスを実行し、出力ストリームを( "getInputStream"?を介して)取得し、それをバッファーリーダーに配置し、バッファーが空になるまでループするだけです。

ここで「waitfor」スタイルのコマンドが必要ないのはなぜですか。バッファが空になり、ループを終了し、プロセスがまだ進行している間に続行する可能性があるのではないでしょうか。私がそれを実行すると、これはそうではないようです。

いずれにせよ、私はcommons execを使用して同じ動作を取得しようとしています。

DefaultExecuteResultHandler rh = new DefaultExecuteResultHandler();
ExecuteWatchdog wd  = new ExecuteWatchdog( ExecuteWatchdog.INFINITE_TIMEOUT );
Executor exec = new DefaultExecutor();

ByteArrayOutputStream out = new ByteArrayOutputStream();
PumpStreamHandler psh = new PumpStreamHandler( out );

exec.setStreamHandler( psh );
exec.setWatchdog( wd );

exec.execute(cmd, rh );
rh.waitFor();

私はpumpstreamhandlerが何をしているかを理解しようとしています。私はこれがexecオブジェクトからの出力を取得し、Perlスクリプトのstdout/errからのバイトで出力ストリームに書き込むと想定していますか?

もしそうなら、どのようにして出力を行ごとにストリーミングする上記の動作を得るでしょうか?例では、最後にout.toString()を呼び出すことを示していますが、実行するとスクリプトのすべての出力のダンプが得られると思いますか?行ごとに実行されているときに出力を表示するには、どうすればよいでしょうか。

------------今後の編集---------------------

グーグルを介してこれを見つけ、同様にうまくいきます:

public static void main(String a[]) throws Exception
{
    ByteArrayOutputStream stdout = new ByteArrayOutputStream();
    PumpStreamHandler psh = new PumpStreamHandler(stdout);
    CommandLine cl = CommandLine.parse("ls -al");
    DefaultExecutor exec = new DefaultExecutor();
    exec.setStreamHandler(psh);
    exec.execute(cl);
    System.out.println(stdout.toString());
}
27
James DeRagon

ByteArrayOutputStreamPumpStreamHandlerに渡さないでください。抽象クラス_org.Apache.commons.exec.LogOutputStream_の実装を使用してください。 javadoc から:

実装は入力データを解析して行を作成し、完全な行をユーザー定義の実装に渡します。

したがって、LogOutputStramは出力を前処理して、生のバイトではなく個々の行の処理を制御できるようにします。このようなもの:

_import Java.util.LinkedList;
import Java.util.List;
import org.Apache.commons.exec.LogOutputStream;

public class CollectingLogOutputStream extends LogOutputStream {
    private final List<String> lines = new LinkedList<String>();
    @Override protected void processLine(String line, int level) {
        lines.add(line);
    }   
    public List<String> getLines() {
        return lines;
    }
}
_

次に、_exec.execute_へのブロッキング呼び出しの後、getLines()には、探している標準出力と標準エラーが含まれます。 ExecutionResultHandlerは、プロセスを実行し、すべてのstdOut/stdErrを行のリストに収集するという観点からはオプションです。

30
James A Wilson

ここで「ウェイト」スタイルのコマンドが必要ないのはなぜですか。バッファが空になり、ループを終了して、プロセスがまだ進行している間に続行する可能性があるのではないでしょうか。私がそれを実行すると、これはそうではないようです。

readLineブロック。つまり、コードは行が読み取られるまで待機します。

PumpStreamHandler

から Documentation

サブプロセスの標準出力とエラーを親プロセスの標準出力とエラーにコピーします。出力ストリームまたはエラーストリームがnullに設定されている場合、そのストリームからのフィードバックは失われます。

3
Hyangelo

James A Wilsonの回答に基づいて、ヘルパークラス「Execute」を作成しました。便宜上、exitValueも提供するソリューションに彼の答えをラップします。

この方法でコマンドを実行するには、1行が必要です。

ExecResult result=Execute.execCmd(cmd,expectedExitCode);

次のJunitテストケースは、テストしてその使用方法を示しています。

Junit4テストケース:

package com.bitplan.newsletter;

import static org.junit.Assert.*;

import Java.util.List;

import org.junit.Test;

import com.bitplan.cmd.Execute;
import com.bitplan.cmd.Execute.ExecResult;

/**
 * test case for the execute class
 * @author wf
 *
 */
public class TestExecute {
     @Test
   public void testExecute() throws Exception {
     String cmd="/bin/ls";
     ExecResult result = Execute.execCmd(cmd,0);
     assertEquals(0,result.getExitCode());
     List<String> lines = result.getLines();
     assertTrue(lines.size()>0);
     for (String line:lines) {
         System.out.println(line);
     }
   }
}

実行Javaヘルパークラス:

package com.bitplan.cmd;

import Java.util.LinkedList;
import Java.util.List;
import Java.util.logging.Level;

import org.Apache.commons.exec.CommandLine;
import org.Apache.commons.exec.DefaultExecutor;
import org.Apache.commons.exec.LogOutputStream;
import org.Apache.commons.exec.PumpStreamHandler;

/**
 * Execute helper using Apache commons exed
 *
 *  add this dependency to your pom.xml:
   <dependency>
            <groupId>org.Apache.commons</groupId>
            <artifactId>commons-exec</artifactId>
            <version>1.2</version>
        </dependency>

 * @author wf
 *
 */
public class Execute {

    protected static Java.util.logging.Logger LOGGER = Java.util.logging.Logger
            .getLogger("com.bitplan.cmd");

    protected final static boolean debug=true;

    /**
     * LogOutputStream
     * http://stackoverflow.com/questions/7340452/process-output-from
     * -Apache-commons-exec
     * 
     * @author wf
     * 
     */
    public static class ExecResult extends LogOutputStream {
        private int exitCode;
        /**
         * @return the exitCode
         */
        public int getExitCode() {
            return exitCode;
        }

        /**
         * @param exitCode the exitCode to set
         */
        public void setExitCode(int exitCode) {
            this.exitCode = exitCode;
        }

        private final List<String> lines = new LinkedList<String>();

        @Override
        protected void processLine(String line, int level) {
            lines.add(line);
        }

        public List<String> getLines() {
            return lines;
        }
    }

    /**
     * execute the given command
     * @param cmd - the command 
     * @param exitValue - the expected exit Value
     * @return the output as lines and exit Code
     * @throws Exception
     */
    public static ExecResult execCmd(String cmd, int exitValue) throws Exception {
        if (debug)
            LOGGER.log(Level.INFO,"running "+cmd);
        CommandLine commandLine = CommandLine.parse(cmd);
        DefaultExecutor executor = new DefaultExecutor();
        executor.setExitValue(exitValue);
        ExecResult result =new ExecResult();
        executor.setStreamHandler(new PumpStreamHandler(result));
        result.setExitCode(executor.execute(commandLine));
        return result;
    }

}
2
Wolfgang Fahl

非常に古いスレッドですが、Apache Commons Execを使用する必要があり、同じ問題を解決する必要がありました。 2014年に公開されたApache Commons Execの最新バージョンを信頼しています。以下のソリューションは、ウォッチドッグの有無にかかわらずうまく機能します。

class CollectingLogOutputStream implements ExecuteStreamHandler {
private final List<String> lines = new LinkedList<String>();
public void setProcessInputStream(OutputStream outputStream) throws IOException 
{
}
//important - read all output line by line to track errors
public void setProcessErrorStream(InputStream inputStream) throws IOException {
    InputStreamReader isr = new InputStreamReader(inputStream);
    BufferedReader br = new BufferedReader(isr);
    String line="";
    while( (line = br.readLine()) != null){
        //use lines whereever you want - for now just print on console
        System.out.println("error:"+line);
    }
}
//important - read all output line by line to track process output
public void setProcessOutputStream(InputStream inputStream) throws IOException 
{
    InputStreamReader isr = new InputStreamReader(inputStream);
    BufferedReader br = new BufferedReader(isr);
    String line="";
    while( (line = br.readLine()) != null){
        //use lines whereever you want - for now just print on console
        System.out.println("output:"+line);
    }
  }

public void start() throws IOException {
}

public void stop() throws IOException {
}
}

上記のクラスは、以下のようにexecutorのStreamHandlerとして設定できます。

//set newly created class stream handler for the executor
executor.setStreamHandler(new CollectingLogOutputStream());

完全なコードはこちらから入手できます。 https://github.com/raohammad/externalprocessfromjava

0
khawarizmi