Tout d'abord, je recommande 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 par rapport à Runtime.getRuntime().exec()
est qu'il vous permet de rediriger l'erreur standard du processus enfant vers sa sortie standard. Cela signifie que vous n'avez qu'un seul InputStream
à lire. Avant cela, vous deviez avoir deux Threads séparés, l'un lisant à partir de stdout
et une lecture de stderr
afin d'éviter que le tampon d'erreur standard ne se remplisse alors que le tampon de sortie standard est 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);
}
ne sortent que lorsque le reader
qui lit la sortie standard du processus, retourne la fin du fichier. Cela ne se produit que lorsque le bash
le processus sort. Il ne retournera pas la fin du fichier s'il n'y a plus de sortie du processus. Au lieu de cela, elle attendra la prochaine ligne de sortie du processus et ne reviendra pas avant d'avoir obtenu cette ligne.
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à à attendre 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
avec cmd.exe
mais les principes devraient être les mêmes), et j'ai trouvé que :
- après avoir tapé 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, puisque l'option cmd.exe
a quitté le processus. Le programme demande alors une autre ligne d'entrée (qui est ignorée), passe directement à la deuxième boucle puisque le processus enfant est déjà sorti, puis se termine lui-même.
- si je tape dans
exit
et ensuite echo test
j'obtiens une IOException se plaignant de la fermeture d'un tube. C'est normal - la première ligne d'entrée a provoqué la sortie 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. Ce programme conservait un certain nombre de shells, exécutait des commandes dans ceux-ci et lisait la sortie de ces commandes. L'astuce utilisée consistait à toujours écrire une ligne "magique" qui marque la fin de la sortie de la commande shell, et à 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 remplacé tout ce qui se trouve après la ligne qui assigne à writer
avec 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, j'ai pu exécuter quelques commandes de manière fiable et obtenir les résultats de chacune d'entre elles individuellement.
Les deux echo --EOF--
dans la ligne envoyée au shell sont là pour s'assurer que la sortie de la commande est terminée par --EOF--
même à la suite d'une erreur de la commande.
Bien sûr, cette approche a ses limites. Ces limites comprennent :
- si j'entre une commande qui attend une entrée 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,
- il s'embrouille un peu si la commande exécutée par le shell écrit une ligne
--EOF--
.
-
bash
signale une erreur de syntaxe et se termine si vous entrez du texte avec une erreur de correspondance. )
.
Ces points peuvent ne pas avoir d'importance pour vous si ce que vous envisagez d'exécuter en tant que tâche planifiée est limité à une commande ou à un petit ensemble de commandes qui ne se comporteront jamais de manière aussi pathologique.
EDIT : amélioration de la gestion des sorties et autres changements mineurs suite à l'exécution sous Linux.
0 votes
"Broken pipe" signifie probablement que le processus enfant a quitté. Je n'ai pas entièrement examiné le reste de votre code pour voir quels sont les autres problèmes.
1 votes
Utilisez des fils séparés, cela fonctionnera très bien.