ProcessBuilder: la Redirection de stdout et stderr de commencé le processus sans bloquer le thread principal
Je suis en train de construire 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 saisir tout ce qui se passe à travers stdout et/ou stderr de ce processus et de le rediriger vers System.out
de manière asynchrone. Je veux le processus et ses redirection de la sortie d'exécuter en arrière-plan. Jusqu'à présent, le seul moyen que j'ai trouvé à faire c'est manuellement lancer un nouveau thread continue de lire à partir de stdOut
et ensuite appeler le write()
méthode 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();
Alors que l'approche par type de travaux, il se sent un peu sale. Et en plus de cela, il me donne un fil en plus de gérer et de mettre fin correctement. Est-il meilleure façon de le faire?
- Si le blocage du thread appelant était une option, il y aurait une solution très simple, même dans la version 6 de Java:
org.apache.commons.io.IOUtils.copy(new ProcessBuilder().command(commandLine) .redirectErrorStream(true).start().getInputStream(), System.out);
Vous devez vous connecter pour publier un commentaire.
À seulement une façon de Java 6 ou une version antérieure est avec un soi-disant
StreamGobbler
(qui vous commencé à créer):...
Pour Java 7, voir Evgeniy Dorofeev de réponse.
Utilisation
ProcessBuilder.inheritIO
, il définit la source et la destination sous-processus standard d'e/S d'être les mêmes que ceux de l'actuel processus Java.Si Java 7 n'est pas une option
Fils mourra automatiquement lorsque le processus secondaire terminée, car
src
sera EOF.inheritIO()
ou de l'une de ces pratiqueredirect*(ProcessBuilder.Redirect)
méthodes prochaine fois que j'ai besoin de faire ça sur une java 7 projet. Malheureusement, mon projet est de java 6.sc
doit être fermé?Une solution flexible avec Java 8 lambda qui vous permet de fournir un
Consumer
qui permettra de traiter la sortie (par exemple. les enregistrer dans un fichier ligne par ligne.run()
est un one-liner avec pas vérifié les exceptions levées. Sinon pour la mise en œuvre deRunnable
, elle peut s'étendreThread
plutôt que d'autres réponses suggèrent.Vous pouvez alors l'utiliser par exemple comme ceci:
Ici le flux de sortie est redirigée vers
System.out
et le flux d'erreur est enregistré sur le niveau d'erreur par lalogger
.forEach()
dans lerun()
méthode bloquera jusqu'à ce que le flux de données est ouverte, dans l'attente de la prochaine ligne. Elle va sortir quand le volet est fermé.C'est aussi simple que suivant:
par .redirectErrorStream(vrai) vous dites processus de de fusion d'erreur et de flux de sortie puis par .redirectOutput(fichier) vous rediriger fusionné sortie vers un fichier.
Mise à jour:
J'ai réussi à le faire comme suit:
Maintenant, vous êtes en mesure de voir les deux sorties principales et asyncOut threads dans Système.hors
Moi aussi, je peux utiliser uniquement la version 6 de Java. J'ai utilisé @EvgeniyDorofeev fil du scanneur de mise en œuvre. Dans mon code, après un processus de finitions, j'ai pour exécuter immédiatement deux autres processus que chaque comparer la redirection de la sortie (un diff unité de test pour s'assurer stdout et stderr sont les mêmes que le bienheureux).
Le scanner fils n'avez pas fini assez vite, même si je waitFor() la fin du processus. Pour que le code fonctionne correctement, je dois veiller à ce que les fils sont joints une fois le processus terminé.
Votre code personnalisé va de la place de la
...
Par défaut, à la création de la sous-processus ne dispose pas de son propre terminal ou une console. Tous ses I/O standard (c'est à dire stdin, stdout, stderr) devront être redirigé vers le processus parent, où ils peuvent être consultés via le flux obtenus en utilisant les méthodes getOutputStream(), getInputStream(), et getErrorStream(). Le processus parent utilise ces flux d'entrée d'alimentation et obtenir de sortie de la sous-processus. Parce que certains indigènes les plates-formes offrent peu de taille de la mémoire tampon pour l'entrée standard et les flux de sortie, le défaut de rapidement écrire le flux d'entrée ou de lire le flux de sortie de la sous-processus peut provoquer le sous-processus de bloquer, voire de blocage.
https://www.securecoding.cert.org/confluence/display/java/FIO07-J.+Do+not+let+external+processes+block+on+IO+buffers