Sortie du processus apache-commons exec

je suis à bout. Je suis sûr que c'est quelque chose de simple et j'ai très probablement d'énormes trous dans ma compréhension de java et des flux. Je pense qu'il y a tellement de classes que je suis un peu submergé d'essayer de fouiller à travers L'API pour savoir quand et comment je veux utiliser la multitude de flux d'entrées/sorties.

je viens d'apprendre l'existence de la bibliothèque apache commons (auto-enseignement java fail), et je suis en train d'essayer de convertir certains de mes Runtime.getRuntime ().exec pour utiliser les communes - exec. Déjà il est corrigé une partie de la fois tous les 6 mois ce problème apparaît puis disparaît les problèmes de style avec exec.

le code exécute un script perl, et affiche le stdout du script dans l'interface graphique pendant qu'il est en cours d'exécution.

Le code d'appel est à l'intérieur d'un swingworker.

je suis perdu comment utiliser le pumpStreamHandler... en tout cas voici l'ancien code:

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

je suppose que c'est ce que j'obtiens pour copier du code que je ne comprends pas complètement il y a longtemps. Ce qui précède, je suppose, exécute le processus, puis saisit le flux sortant (via "getInputStream"?), le place dans un lecteur tamponné, puis fera simplement une boucle jusqu'à ce que le tampon soit vide.

ce que je ne comprends pas c'est pourquoi il n'y a pas besoin d'une commande de style 'waitfor' ici? N'est-il pas possible qu'il y ait un moment où le tampon sera vide, en boucle, et continuer pendant que le processus est encore en cours? Quand je le lance, cela ne semble pas être le cas.

dans tous les cas, je suis en train d'essayer d'obtenir le même comportement en utilisant commons exec, essentiellement à nouveau aller à partir de google Code trouvé:

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

j'essaie de comprendre ce que fait pumpstreamhandler. Je suppose que cela va prendre la sortie de l'objet exec, et remplir la sortie que je lui fournis avec les octets du script perl stdout / err?

si oui, comment obtiendriez-vous le comportement ci-dessus pour qu'il diffuse la ligne de sortie par ligne? Dans les exemples les gens montrent que vous appelez l'extérieur.toString () à la fin, et je suppose que cela me donnerait juste un dump de toute la sortie du script une fois qu'il est fait en cours d'exécution? Comment le feriez-vous de telle sorte qu'il montre la sortie comme il exécute ligne par ligne?

------------L'Avenir Modifier ---------------------

a trouvé ce via google et fonctionne nice ainsi:

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());
}
25
demandé sur James DeRagon 2011-09-08 00:57:33

3 réponses

Ne passe pas ByteArrayOutputStream à la PumpStreamHandler , l'utilisation d'une implémentation de la classe abstraite org.apache.commons.exec.LogOutputStream . Quelque chose comme ceci:

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

Puis, après l'appel de blocage exec.execute votre getLines() aura la norme et standard d'erreur que vous recherchez. Le ExecutionResultHandler est facultatif du point de vue de l'exécution du processus, et de la collecte de tous les stdOut/stdErr dans une liste de lignes.

28
répondu James A Wilson 2016-07-30 14:42:51

ce que je ne comprends pas c'est pourquoi il n'y a pas besoin d'une commande de style 'waitfor' ici? N'est-il pas possible qu'il y ait un moment où le tampon soit vide, quitte la boucle, et continue pendant que le processus est en cours? Quand je le lance, cela ne semble pas être le cas.

readLine blocs. C'est-à-dire que votre code attendra qu'une ligne ait été lue.

PumpStreamHandler

de l' Documentation

copie la sortie standard et l'erreur des sous-processus à la sortie standard et erreur du processus parent. Si la sortie ou le flux d'erreur sont définis à null, tout feedback de ce flux sera perdu.

3
répondu Hyangelo 2011-09-07 21:09:51

basé sur la réponse de James A. Wilson j'ai créé la classe helper "Execute". Il enveloppe sa réponse dans une solution qui fournit également la valeur exit pour la commodité.

Une seule ligne est nécessaire pour exécuter une commande de cette façon:

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

les essais Junit Testcase suivants et montre comment l'utiliser:

Junit4 cas de test:

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

Exécuter Java de la Classe helper:

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
     * /q/process-output-from-apache-commons-exec-49838/"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;
    }

}
1
répondu Wolfgang Fahl 2014-07-31 10:22:24