Tout d'abord, je recommanderais de remplacer la ligne
Process process = Runtime.getRuntime ().exec ("/bin/bash");
avec les lignes
ProcessBuilder builder = new ProcessBuilder("/bin/bash");
builder.redirectErrorStream(true);
Process process = builder.start();
ProcessBuilder est nouveau dans Java 5 et facilite l'exécution des processus externes. À mon avis, son amélioration la plus significative Runtime.getRuntime().exec()
est qu'il vous permet de rediriger l'erreur standard du processus enfant dans sa sortie standard. Cela signifie que vous n'en avez qu'un seul InputStream
à lire. Avant cela, vous deviez avoir deux threads séparés, une lecture à partir de stdout
et une lecture à partir de stderr
, pour éviter que le tampon d'erreur standard ne se remplisse alors que le tampon de sortie standard était vide (provoquant le blocage du processus enfant), ou vice versa.
Ensuite, les boucles (dont vous avez deux)
while ((line = reader.readLine ()) != null) {
System.out.println ("Stdout: " + line);
}
quitte uniquement lorsque le reader
, qui lit la sortie standard du processus, retourne la fin du fichier. Cela se produit uniquement lorsque le bash
processus se termine. Il ne retournera pas de fin de fichier s'il se trouve actuellement qu'il n'y a plus de sortie du processus. Au lieu de cela, il attendra la prochaine ligne de sortie du processus et ne retournera pas avant d'avoir cette ligne suivante.
Puisque vous envoyez deux lignes d'entrée au processus avant d'atteindre cette boucle, la première de ces deux boucles se bloquera si le processus n'est pas sorti après ces deux lignes d'entrée. Il restera là en attendant qu'une autre ligne soit lue, mais il n'y aura jamais d'autre ligne à lire.
J'ai compilé votre code source (je suis sous Windows pour le moment, donc j'ai remplacé /bin/bash
par cmd.exe
, mais les principes devraient être les mêmes), et j'ai trouvé que:
- après avoir tapé sur deux lignes, la sortie des deux premières commandes apparaît, mais le programme se bloque,
- si je tape, disons,
echo test
et ensuite exit
, le programme sort de la première boucle depuis la fin du cmd.exe
processus. Le programme demande alors une autre ligne d'entrée (qui est ignorée), saute directement sur la deuxième boucle puisque le processus fils est déjà sorti, puis se ferme.
- si je tape
exit
et puis echo test
, j'obtiens une IOException se plaignant de la fermeture d'un tuyau. Il faut s'y attendre - la première ligne d'entrée a entraîné la fermeture du processus et il n'y a nulle part où envoyer la deuxième ligne.
J'ai vu une astuce qui fait quelque chose de similaire à ce que vous semblez vouloir, dans un programme sur lequel je travaillais auparavant. Ce programme conservait un certain nombre de shells, y exécutait des commandes et lisait la sortie de ces commandes. L'astuce utilisée était de toujours écrire une ligne «magique» qui marque la fin de la sortie de la commande shell, et de l'utiliser pour déterminer quand la sortie de la commande envoyée au shell était terminée.
J'ai pris votre code et j'ai tout remplacé après la ligne affectée writer
par la boucle suivante:
while (scan.hasNext()) {
String input = scan.nextLine();
if (input.trim().equals("exit")) {
// Putting 'exit' amongst the echo --EOF--s below doesn't work.
writer.write("exit\n");
} else {
writer.write("((" + input + ") && echo --EOF--) || echo --EOF--\n");
}
writer.flush();
line = reader.readLine();
while (line != null && ! line.trim().equals("--EOF--")) {
System.out.println ("Stdout: " + line);
line = reader.readLine();
}
if (line == null) {
break;
}
}
Après avoir fait cela, je pourrais exécuter de manière fiable quelques commandes et que la sortie de chacune me revienne individuellement.
Les deux echo --EOF--
commandes dans la ligne envoyée au shell sont là pour garantir que la sortie de la commande se termine avec --EOF--
même le résultat d'une erreur de la commande.
Bien entendu, cette approche a ses limites. Ces limitations incluent:
- si j'entre une commande qui attend l'entrée de l'utilisateur (par exemple un autre shell), le programme semble se bloquer,
- il suppose que chaque processus exécuté par le shell termine sa sortie par une nouvelle ligne,
- cela devient un peu confus si la commande exécutée par le shell arrive à écrire une ligne
--EOF--
.
bash
signale une erreur de syntaxe et quitte si vous entrez du texte avec un )
.
Ces points peuvent ne pas vous intéresser si tout ce que vous envisagez d'exécuter en tant que tâche planifiée va être limité à une commande ou à un petit ensemble de commandes qui ne se comporteront jamais de manière aussi pathologique.
EDIT : améliorez la gestion des sorties et d'autres modifications mineures suite à son exécution sous Linux.