In primo luogo, consiglierei di sostituire la linea
Process process = Runtime.getRuntime ().exec ("/bin/bash");
con le linee
ProcessBuilder builder = new ProcessBuilder("/bin/bash");
builder.redirectErrorStream(true);
Process process = builder.start();
ProcessBuilder è una novità di Java 5 e semplifica l'esecuzione di processi esterni. A mio parere, il suo miglioramento più significativo Runtime.getRuntime().exec()è che consente di reindirizzare l'errore standard del processo figlio nel suo output standard. Ciò significa che ne hai solo uno InputStreamda cui leggere. Prima di questo, era necessario disporre di due Thread separati, uno da lettura stdoute uno da lettura stderr, per evitare il riempimento del buffer di errore standard mentre il buffer di output standard era vuoto (causando il blocco del processo figlio) o viceversa.
Successivamente, i loop (di cui hai due)
while ((line = reader.readLine ()) != null) {
System.out.println ("Stdout: " + line);
}
esce solo quando reader, che legge dallo standard output del processo, restituisce end-of-file. Questo accade solo quando il bashprocesso termina. Non restituirà la fine del file se al momento non ci sono più output dal processo. Invece, attenderà la prossima riga di output dal processo e non tornerà finché non avrà questa riga successiva.
Poiché stai inviando due linee di input al processo prima di raggiungere questo ciclo, il primo di questi due cicli si bloccherà se il processo non è terminato dopo queste due linee di input. Rimarrà lì in attesa che venga letta un'altra riga, ma non ci sarà mai un'altra riga da leggere.
Ho compilato il tuo codice sorgente (sono su Windows al momento, quindi l'ho sostituito /bin/bashcon cmd.exe, ma i principi dovrebbero essere gli stessi), e ho scoperto che:
- dopo aver digitato due righe, appare l'output dei primi due comandi, ma poi il programma si blocca,
- se digito, ad esempio,
echo teste poi exit, il programma esce dal primo ciclo da quando il cmd.exeprocesso è terminato. Il programma quindi richiede un'altra riga di input (che viene ignorata), salta direttamente il secondo ciclo poiché il processo figlio è già terminato e quindi esce da solo.
- se digito
exite poi echo test, ottengo una IOException che si lamenta della chiusura di una pipe. Questo è prevedibile: la prima riga di input ha causato l'uscita del processo e non c'è nessun posto dove inviare la seconda riga.
Ho visto un trucco che fa qualcosa di simile a quello che sembri desiderare, in un programma su cui lavoravo. Questo programma ha mantenuto un certo numero di shell, ha eseguito comandi in esse e ha letto l'output di questi comandi. Il trucco utilizzato è stato quello di scrivere sempre una riga "magica" che segna la fine dell'output del comando della shell e utilizzarla per determinare quando l'output del comando inviato alla shell era terminato.
Ho preso il tuo codice e ho sostituito tutto dopo la riga che assegna a writercon il seguente ciclo:
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;
}
}
Dopo aver fatto ciò, potrei eseguire in modo affidabile alcuni comandi e ricevere l'output di ciascuno di essi individualmente.
I due echo --EOF--comandi nella riga inviata alla shell servono a garantire che l'output del comando venga terminato --EOF--anche in caso di errore del comando.
Ovviamente questo approccio ha i suoi limiti. Queste limitazioni includono:
- se inserisco un comando che attende l'input dell'utente (ad esempio un'altra shell), il programma sembra bloccarsi,
- assume che ogni processo eseguito dalla shell termini il suo output con una nuova riga,
- diventa un po 'confuso se il comando eseguito dalla shell scrive una riga
--EOF--.
bashsegnala un errore di sintassi ed esce se si immette del testo con un'estensione senza corrispondenza ).
Questi punti potrebbero non essere importanti per te se qualunque cosa tu stia pensando di eseguire come attività pianificata sarà limitata a un comando o un piccolo insieme di comandi che non si comporteranno mai in modi così patologici.
MODIFICA : migliora la gestione delle uscite e altre modifiche minori dopo l'esecuzione su Linux.