Je construis un processus en Java à l'aide de ProcessBuilder comme suit:
ProcessBuilder pb = new ProcessBuilder()
.command("somecommand", "arg1", "arg2")
.redirectErrorStream(true);
Process p = pb.start();
InputStream stdOut = p.getInputStream();
Maintenant, mon problème est le suivant: je voudrais capturer tout ce qui passe par stdout et / ou stderr de ce processus et le rediriger de System.out
manière asynchrone. Je veux que le processus et sa redirection de sortie s'exécutent en arrière-plan. Jusqu'à présent, le seul moyen que j'ai trouvé pour le faire est de créer manuellement un nouveau thread qui lira en continu stdOut
et appellera la write()
méthode appropriée de System.out
.
new Thread(new Runnable(){
public void run(){
byte[] buffer = new byte[8192];
int len = -1;
while((len = stdOut.read(buffer)) > 0){
System.out.write(buffer, 0, len);
}
}
}).start();
Bien que cette approche fonctionne, elle semble un peu sale. Et en plus de cela, cela me donne un fil de plus à gérer et à terminer correctement. Y a-t-il une meilleure façon de faire cela?
java
processbuilder
LordOfThePigs
la source
la source
org.apache.commons.io.IOUtils.copy(new ProcessBuilder().command(commandLine) .redirectErrorStream(true).start().getInputStream(), System.out);
Réponses:
Le seul moyen dans Java 6 ou antérieur est avec un soi-disant
StreamGobbler
(que vous commencez à créer):...
Pour Java 7, voir la réponse d'Evgeniy Dorofeev.
la source
À utiliser
ProcessBuilder.inheritIO
, il définit la source et la destination des E / S standard du sous-processus comme celles du processus Java actuel.Si Java 7 n'est pas une option
Les threads mourront automatiquement à la fin du sous-processus, car
src
EOF.la source
inheritIO()
ou l'une de cesredirect*(ProcessBuilder.Redirect)
méthodes pratiques la prochaine fois que j'aurai besoin de le faire sur un projet java 7. Malheureusement, mon projet est java 6.sc
doit être fermé?Une solution flexible avec Java 8 lambda qui vous permet de fournir un
Consumer
qui traitera la sortie (par exemple, la consigner) ligne par ligne.run()
est une ligne unique sans exception vérifiée lancée. Alternativement à la mise en œuvreRunnable
, il peut s'étendre à laThread
place comme le suggèrent d'autres réponses.Vous pouvez ensuite l'utiliser par exemple comme ceci:
Ici, le flux de sortie est redirigé vers
System.out
et le flux d'erreur est enregistré au niveau d'erreur par lelogger
.la source
forEach()
dans larun()
méthode se bloquera jusqu'à ce que le flux soit ouvert, en attendant la ligne suivante. Il se fermera lorsque le flux sera fermé.C'est aussi simple que de suivre:
par .redirectErrorStream (true) vous indiquez au processus de fusionner l'erreur et le flux de sortie , puis par .redirectOutput (fichier) vous redirigez la sortie fusionnée vers un fichier.
Mettre à jour:
J'ai réussi à le faire comme suit:
Vous pouvez maintenant voir les deux sorties des threads principal et asyncOut dans System.out
la source
Solution java8 simple avec capture des sorties et traitement réactif en utilisant
CompletableFuture
:Et l'utilisation:
la source
Il existe une bibliothèque qui fournit un meilleur ProcessBuilder, zt-exec. Cette bibliothèque peut faire exactement ce que vous demandez et plus encore.
Voici à quoi ressemblerait votre code avec zt-exec au lieu de ProcessBuilder:
ajoutez la dépendance:
Le code :
La documentation de la bibliothèque est ici: https://github.com/zeroturnaround/zt-exec/
la source
Moi aussi, je ne peux utiliser que Java 6. J'ai utilisé l'implémentation du scanner de threads de @ EvgeniyDorofeev. Dans mon code, une fois le processus terminé, je dois exécuter immédiatement deux autres processus qui comparent chacun la sortie redirigée (un test unitaire basé sur des différences pour s'assurer que stdout et stderr sont les mêmes que les bénis).
Les threads du scanner ne se terminent pas assez tôt, même si j'attends () la fin du processus. Pour que le code fonctionne correctement, je dois m'assurer que les threads sont joints une fois le processus terminé.
la source
En plus de la réponse msangel, je voudrais ajouter le bloc de code suivant:
Il permet de rediriger le flux d'entrée (stdout, stderr) du processus vers un autre consommateur. Cela peut être System.out :: println ou tout autre élément consommant des chaînes.
Usage:
la source
Votre code personnalisé va à la place du
...
la source
Par défaut, le sous-processus créé n'a pas son propre terminal ou console. Toutes ses opérations d'E / S standard (c'est-à-dire stdin, stdout, stderr) seront redirigées vers le processus parent, où elles seront accessibles via les flux obtenus en utilisant les méthodes getOutputStream (), getInputStream () et getErrorStream (). Le processus parent utilise ces flux pour alimenter en entrée et obtenir la sortie du sous-processus. Étant donné que certaines plates-formes natives ne fournissent qu'une taille de tampon limitée pour les flux d'entrée et de sortie standard, l'échec de l'écriture rapide du flux d'entrée ou de la lecture du flux de sortie du sous-processus peut entraîner le blocage du sous-processus, voire un blocage.
https://www.securecoding.cert.org/confluence/display/java/FIO07-J.+Do+not+let+external+processes+block+on+IO+buffers
la source