How to Run a Shell Command in Java

Overview

W tym tutorialu zilustrujemy dwa sposoby wykonania polecenia powłoki z kodu Java.

Pierwszym jest użycie klasy Runtime i wywołanie jej metody exec.

Drugim i bardziej konfigurowalnym sposobem, będzie stworzenie i użycie instancji ProcessBuilder.

Zależność od systemu operacyjnego

Zanim utworzymy nowy proces wykonujący nasze polecenie powłoki, musimy najpierw określić system operacyjny, na którym działa nasza maszyna JVM.

To dlatego, że w systemie Windows musimy uruchomić nasze polecenie jako argument powłoki cmd.exe, a na wszystkich innych systemach operacyjnych możemy wydać standardową powłokę, zwaną sh:

boolean isWindows = System.getProperty("os.name") .toLowerCase().startsWith("windows");

Wejście i wyjście

Ponadto potrzebujemy sposobu, aby podłączyć się do strumieni wejścia i wyjścia naszego procesu.

Przynajmniej wyjście musi zostać skonsumowane – w przeciwnym razie nasz proces nie wróci pomyślnie, zamiast tego zawiesi się.

Zaimplementujmy powszechnie używaną klasę o nazwie StreamGobbler, która konsumuje strumień wejściowy:

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); }}

UWAGA: Ta klasa implementuje interfejs Runnable, co oznacza, że może być wykonana przez dowolnego Executora.

Runtime.exec()

Wywołanie metody Runtime.exec() jest prostym, jeszcze nie konfigurowalnym, sposobem na wywołanie nowego podprocesu.

W poniższym przykładzie zażądamy listy katalogów użytkownika i wydrukujemy ją na konsoli:

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

Do drugiej implementacji naszego problemu obliczeniowego użyjemy ProcessBuildera. Jest to preferowane w stosunku do podejścia Runtime, ponieważ jesteśmy w stanie dostosować niektóre szczegóły.

Na przykład jesteśmy w stanie:

  • zmienić katalog roboczy, w którym działa nasze polecenie powłoki, używając builder.directory()
  • ustawić niestandardową mapę klucz-wartość jako środowisko używając builder.environment()
  • przekierować strumienie wejściowe i wyjściowe do niestandardowych zamienników
  • dziedziczyć oba z nich do strumieni bieżącego procesu JVM używając 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;

Zakończenie

Jak widzieliśmy w tym krótkim tutorialu, możemy wykonać polecenie powłoki w Javie na dwa różne sposoby.

Ogólnie, jeśli planujesz dostosować wykonanie spawnu procesu, na przykład, aby zmienić jego katalog roboczy, powinieneś rozważyć użycie ProcessBuilder.

Dodaj komentarz

Twój adres e-mail nie zostanie opublikowany.