Aggiornamento: Questo articolo è stato sostituito dal mio nuovo articolo “Java exec con ProcessBuilder e Process”. Mentre il codice Java mostrato in questo tutorial funziona su semplici casi di “Java exec”, il nuovo articolo mostra come leggere correttamente i flussi di output dal vostro comando di sistema nei thread Java, e anche come scrivere nello standard input del vostro comando, se necessario.

Sentitevi liberi di leggere questo articolo per informazioni di background/legacy, ma vi consiglio vivamente di usare il codice sorgente che sto condividendo nel mio nuovo articolo “Java exec”, perché risolve i problemi di standard input, output ed errori che non ho gestito correttamente nel codice sottostante.

Introduzione

Ho letto molto su Java ma una delle cose che vedo raramente discussa è come si dovrebbe procedere per eseguire comandi di sistema esterni. Naturalmente, probabilmente non si legge molto su questo argomento perché toglie portabilità alle applicazioni Java. Per esempio, se scrivete un’applicazione Java su un sistema Unix, potreste essere interessati ad eseguire il comando “ps -ef” e leggere l’output del comando. Per i sistemi Unix questo è ottimo, ma sfortunatamente, questo stesso programma non funzionerà su un sistema Windows perché il comando ps non è disponibile su Windows.

Bene, dimenticheremo la portabilità per questo articolo, e dimostreremo un metodo che può essere usato per eseguire i comandi di sistema. Abbiamo ricevuto molte richieste su questo argomento, quindi ecco qui.

Discussione (Runtime exec e Process)

Eseguire un comando di sistema è relativamente semplice – una volta che lo avete visto fare la prima volta. Implica l’uso di due classi Java, la classe Runtime e la classe Process. Fondamentalmente, si usa il metodo exec della classe Runtime per eseguire il comando come un processo separato. Invocando il metodo exec si restituisce un oggetto Process per gestire il sottoprocesso. Poi si usano i metodi getInputStream() e getErrorStream() dell’oggetto Process per leggere l’output normale del comando e l’output di errore del comando. Quello che fate con l’output del comando eseguito dipende interamente da voi e dall’applicazione che state creando.

(Nota: c’è anche un metodo getOutputStream() che potete usare per scrivere al processo, ma non lo copriremo in questo articolo. Tratteremo questo e alcune altre caratteristiche avanzate in un articolo futuro.)

Un esempio di Java exec

Il codice mostrato nel listato 1 fornisce un esempio funzionante della nostra tecnica “Java exec” in un file chiamato JavaRunCommand.java.

import java.io.*;public class JavaRunCommand { public static void main(String args) { String s = null; try { // run the Unix "ps -ef" command // using the Runtime exec method: Process p = Runtime.getRuntime().exec("ps -ef"); BufferedReader stdInput = new BufferedReader(new InputStreamReader(p.getInputStream())); BufferedReader stdError = new BufferedReader(new InputStreamReader(p.getErrorStream())); // read the output from the command System.out.println("Here is the standard output of the command:\n"); while ((s = stdInput.readLine()) != null) { System.out.println(s); } // read any errors from the attempted command System.out.println("Here is the standard error of the command (if any):\n"); while ((s = stdError.readLine()) != null) { System.out.println(s); } System.exit(0); } catch (IOException e) { System.out.println("exception happened - here's what I know: "); e.printStackTrace(); System.exit(-1); } }}

Listing 1 (sopra): Il file JavaRunCommand.java mostra come potete eseguire un comando di sistema esterno dall’interno di un programma Java.

Come funziona il nostro codice Java exec

La prima cosa che fate è specificare il comando che volete eseguire fornendo questo comando alla classe Runtime. Poiché non potete creare la vostra istanza della classe Runtime, usate prima il metodo getRuntimeper accedere all’ambiente runtime corrente e poi invocate il metodo Runtime exec. Questo restituisce un oggetto Process.

Tutto ciò che fate coinvolge i metodi dell’oggetto Process. In questo caso, poiché stiamo eseguendo il comando “ps -ef” su un sistema Unix, abbiamo solo bisogno di leggere l’output del comando. Leggere l’errore standard probabilmente non è necessario in questo caso, ma ho pensato che almeno valesse la pena mostrarlo, se non come buona pratica di programmazione.

Converto i flussi di input con InputStreamReader e BufferedReader in modo da poter usare il metodo readLine() della classe BufferedReader. Poiché uso queste classi, questa applicazione non verrà compilata correttamente con un vecchio compilatore JDK 1.0.x (queste classi non erano disponibili nella 1.0.x).

Scaricate il codice sorgente dell’esempio “Java exec”

Potrei continuare a lungo su questo argomento, ma la cosa migliore che posso raccomandare è di scaricare il codice sorgente e lavorarci per un po’. Provate ad eseguire diversi comandi per vedere se riuscite a farli funzionare correttamente, e provate ad eseguire un comando che richieda un input (questo sarà un po’ più complicato).