UPDATE: Den här artikeln har ersatts av min nyare artikel ”Java exec with ProcessBuilder and Process”. Medan den Javakod som visas i den här handledningen fungerar på enkla ”Java exec”-fall, visar den nya artikeln hur man korrekt läser utdataströmmarna från ditt systemkommando i Javatrådar, och även hur man skriver till ditt kommandos standardinmatning, om det är nödvändigt.

Känn dig fri att läsa den här artikeln för att få bakgrunds/legitimationsinformation, men jag rekommenderar starkt att du använder källkoden som jag delar med mig av i min nyare ”Java exec”-artikel, eftersom den löser problemen med standardinmatning, utmatning och fel som jag inte hanterade på rätt sätt i koden nedan.

Introduktion

Jag har läst en hel del om Java, men en av de saker som jag sällan ser diskuteras är hur man ska gå tillväga för att köra externa systemkommandon. Naturligtvis läser du förmodligen inte mycket om detta eftersom det tar bort portabiliteten hos Java-applikationer. Om du till exempel skriver en Java-applikation på ett Unix-system kan du vara intresserad av att köra kommandot ”ps -ef” och läsa resultatet av kommandot. För Unix-system är detta utmärkt, men tyvärr kommer samma program inte att fungera på ett Windows-system eftersom ps-kommandot inte är tillgängligt på Windows.

Nja, vi ska glömma portabiliteten för den här artikeln och demonstrera en metod som kan användas för att köra systemkommandon. Vi har fått många förfrågningar om det här ämnet, så här kommer det.

Diskussion (Runtime exec and Process)

Att utföra ett systemkommando är relativt enkelt – när man väl har sett det göras första gången. Det innebär att man använder sig av två Javaklasser, klassen Runtime och klassen Process. I princip använder du exec-metoden i Runtime-klassen för att köra kommandot som en separat process. Om du anropar exec-metoden returnerar du ett Process-objekt för hantering av underprocessen. Sedan använder du metoderna getInputStream() och getErrorStream() i Process-objektet för att läsa den normala utgången av kommandot och felutgången av kommandot. Vad du gör med utdata från det utförda kommandot är helt upp till dig och det program du skapar.

(Obs: Det finns också en getOutputStream()-metod som du kan använda för att skriva till processen, men vi kommer inte att behandla den metoden i den här artikeln. Vi kommer att täcka den och några andra avancerade funktioner i en framtida artikel.)

Ett exempel på Java exec

Koden som visas i Listing 1 ger ett fungerande exempel på vår ”Java exec”-teknik i en fil som heter 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 (ovan): Filen JavaRunCommand.java visar hur du kan köra ett externt systemkommando från ett Java-program.

Hur vår Java exec-kod fungerar

Det första du gör är att specificera kommandot du vill köra genom att lämna detta kommando till Runtime-klassen. Eftersom du inte kan skapa en egen instans av Runtime-klassen använder du först getRuntime-metoden för att få tillgång till den aktuella körtidsmiljön och anropar sedan Runtime exec-metoden. Denna returnerar ett Process-objekt.

Allt annat du gör involverar metoder för Process-objektet. I det här fallet, eftersom vi kör kommandot ”ps -ef” på ett Unix-system, behöver vi bara läsa utdata från kommandot. Att läsa standardfelet är förmodligen inte nödvändigt i det här fallet, men jag tyckte att det åtminstone var värt att visa, om inte god programmeringspraxis.

Jag konverterar inmatningsströmmarna med InputStreamReader och BufferedReader så att jag kan använda readLine()-metoden i klassen BufferedReader. Eftersom jag använder dessa klasser kommer detta program inte att kompileras korrekt med en äldre JDK 1.0.x-kompilator (dessa klasser fanns inte tillgängliga i 1.0.x).

Ladda ner källkoden för exemplet ”Java exec”

Jag skulle kunna fortsätta länge om detta ämne, men det bästa jag kan rekommendera är att du laddar ner källkoden och jobbar med den ett tag. Prova att köra olika kommandon för att se om du kan få dem att fungera korrekt, och försök att köra ett kommando som kräver inmatning (detta blir lite mer komplicerat).