jbang-jash とは
Java でOSコマンドを実行するには、ProcessBuilder
を使います(旧来はRuntime
)。以下のような感じですね。
ProcessBuilder builder = new ProcessBuilder("java", "-version"); Process process = builder.start(); InputStream stream = process.getErrorStream();
コマンドの実行結果は、Process
から標準出力または標準エラー出力を InputStream
として得ることができます(builder.redirectErrorStream(true)
とすることで標準エラー出力を標準出力にマージすることも可能です)。
しかし、InputStream
の取り回しは意外と面倒なものです。
jbang-jash は ProcessBuilder
をラップし、OSコマンドの結果を Java Stream として扱うことを可能とする軽量なライブラリです(jash は、Java + Shell で、Jazz と発音するようです)。
以下のように使うことができます。
$("java -version").stream().forEach(System.out::println);
jbang と付いていますが、JBang とは独立しており、単独のライブラリとして利用することができます。
jbang-jash の使い方
以下の依存を追加します。
dependencies {
implementation("dev.jbang:jash:0.0.3")
}
dev.jbang.jash.Jash
で公開されているスタティックメソッドが入口になるため、以下でインポートしておきます。
import static io.ongres.jash.Jash.*;
デフォルトのシェルでコマンドを実行するには以下のようにします。
$("echo hello").stream().forEach(System.out::println); // hello
$()
は shell()
のエイリアスです。以下でも同様です。
shell("echo hello").stream().forEach(System.out::println); // hello
デフォルトのシェルは、Linux の場合は、SHELL
環境変数から、Windows の場合は ComSpec
環境変数から取得され、一般的には以下のコマンドが実行されることになります。
# Linux /bin/bash -c echo hello # Windows C:\WINDOWS\system32\cmd.exe /C echo hello
ビルダを経由して以下のようにすることもできます。
JashBuilder builder = new JashBuilder("sh").args("-c", "echo hello"); builder.start().stream().forEach(System.out::println);
上記は、Jash.builder()
としてエイリアスされているので、以下と同様です。
Jash.builder("sh", "-c", "echo hello").start() .stream().forEach(System.out::println);
Jash.builder().start()
までを同時に行う Jash.start()
があるので、以下でも同様です。
Jash.start("sh", "-c", "echo hello") .stream().forEach(System.out::println);
しかし、多くの場合は、Jash.shell("..").stream()
だけで十分でしょう。
その他の使い方
pipe()
でパイプ処理できます。
Jash.shell("echo hello; echo world").pipe("cat", "--number") .stream().forEach(System.out::println);
Jash
は AutoCloseable
を実装しているため、通常は以下のように try
句と共に使うことになるでしょう。
try (Jash jash = Jash.shell("echo hello")) { jash.stream().forEach(System.out::println); }
コマンドの終了コードが非ゼロの場合 Jash
は例外を投げます。
終了コードを加味しない場合は withAnyExitCode()
を指定します。
Jash.shell("...").withAnyExitCode().stream()...
許容する終了コードを明示的に指定することもできます。
Jash.shell("...").withAllowedExitCodes(1, 2).stream()...
Jash.withTimeout()
でタイムアウトを指定することができます。
try (var jash = Jash.shell("...").withTimeout(Duration.of(1, ChronoUnit.SECONDS))) { jash.stream()... }