Yleiskatsaus
Tässä opetusohjelmassa esitellään kaksi tapaa suorittaa shell-komento Java-koodista.
Ensimmäinen tapa on käyttää Runtime-luokkaa ja kutsua sen exec-metodia.
Toinen ja helpommin muokattavissa oleva keino on ProcessBuilder-instanssin luominen ja käyttö.
Operating System Dependency
Ennen kuin luomme uuden prosessin, joka suorittaa komentotulkkikomentomme, meidän on ensin määritettävä käyttöjärjestelmä, jossa JVM:mme toimii.
Se johtuu siitä, että Windowsissa komentomme täytyy suorittaa argumenttina cmd.exe-komentokuorta, ja kaikissa muissa käyttöjärjestelmissä voimme antaa vakiokomentokuorta nimeltä sh:
boolean isWindows = System.getProperty("os.name") .toLowerCase().startsWith("windows");
Syöttö ja ulostulo
Lisäksi tarvitsemme tavan kytkeytyä prosessimme syöttö- ja tulostusvirtoihin.
Vähintäänkin ulostulo on kulutettava – muuten prosessimme ei palaa onnistuneesti, vaan se roikkuu.
Toteutetaan yleisesti käytetty luokka nimeltä StreamGobbler, joka kuluttaa InputStreamin:
private static class StreamGobbler implements Runnable { private InputStream inputStream; private Consumer<String> consumer; public StreamGobbler(InputStream inputStream, Consumer<String> consumer) { this.inputStream = inputStream; this.consumer = consumer; } @Override public void run() { new BufferedReader(new InputStreamReader(inputStream)).lines() .forEach(consumer); }}
Huomautus: Tämä luokka toteuttaa Runnable-rajapinnan, mikä tarkoittaa, että se voitaisiin suorittaa millä tahansa Executor-ohjelmalla.
Runime.exec()
Metodin Runtime.exec() kutsuminen on yksinkertainen, ei vielä muokattavissa oleva tapa synnyttää uusi aliprosessi.
Seuraavassa esimerkissä pyydämme hakemistoluettelon käyttäjän kotihakemistosta ja tulostamme sen konsoliin:
String homeDirectory = System.getProperty("user.home");Process process;if (isWindows) { process = Runtime.getRuntime() .exec(String.format("cmd.exe /c dir %s", homeDirectory));} else { process = Runtime.getRuntime() .exec(String.format("sh -c ls %s", homeDirectory));}StreamGobbler streamGobbler = new StreamGobbler(process.getInputStream(), System.out::println);Executors.newSingleThreadExecutor().submit(streamGobbler);int exitCode = process.waitFor();assert exitCode == 0;
ProcessBuilder
Laskentatehtävämme toisessa toteutuksessa käytämme ProcessBuilderia. Tämä on suositeltavampi kuin Runtime-lähestymistapa, koska pystymme muokkaamaan joitakin yksityiskohtia.
Pystymme esimerkiksi:
- muuttamaan työhakemistoa, jossa komentotulkkikomentomme suoritetaan Builderin avulla.directory()
- asettaa mukautetun avain-arvokartan ympäristöksi käyttämällä builder.environment()
- suuntaa input- ja output-virrat mukautettuihin korvaajiin
- periä molemmat nykyisen JVM-prosessin virtoihin käyttämällä builder.inheritIO()
ProcessBuilder builder = new ProcessBuilder();if (isWindows) { builder.command("cmd.exe", "/c", "dir");} else { builder.command("sh", "-c", "ls");}builder.directory(new File(System.getProperty("user.home")));Process process = builder.start();StreamGobbler streamGobbler = new StreamGobbler(process.getInputStream(), System.out::println);Executors.newSingleThreadExecutor().submit(streamGobbler);int exitCode = process.waitFor();assert exitCode == 0;
Johtopäätös
Kuten olemme nähneet tässä pikaoppaassa, voimme suorittaa komentotulkkikomennon Javassa kahdella eri tavalla.
Yleisesti, jos aiot muokata käynnistettävän prosessin suoritusta, esimerkiksi vaihtaa sen työhakemistoa, sinun kannattaa harkita ProcessBuilderin käyttöä.