これは、なにをしたくて書いたもの?
JBangという、Javaプログラムをスクリプト的に実行できるツールがあると知りまして。
ちょっと気になる分野なので、試してみることにしました。
JBang
JBangは、Javaプログラムをスクリプトのように実行できるツールです。
GitHub - jbangdev/jbang: Unleash the power of Java for shell scripting
要件としては、Java(最低8、推奨11)がインストールされていることのようです。
機能としては、
.java
拡張子や.jsh
拡張子のファイルを実行できる- マルチプラットフォーム
- いくつかのパッケージマネージャーでインストール可能
- ライブラリの依存関係を解決可能
- (実験的)ネイティブイメージのサポート
- 利用するJavaのバージョンを指定することができ、そのバージョンがOSにインストールされていない場合は自動的にダウンロードする
などなど。もうちょっと書かれているのですが、詳しくは以下を参照してください。
ポイントは、Mavenなどを使う動機のひとつである、依存関係の解決ができることがこちらに注目した理由ですね。
なお、このツールはこんな感じで、作者の勉強目的で作られているようです。
And to be honest I built jbang just to see if I could and get my Java skills refreshed for the newer features in the language. Use it at your own risk :)
まあ、ちょっと試してみましょう。
環境
今回の環境は、こちら。
$ java --version openjdk 11.0.8 2020-07-14 OpenJDK Runtime Environment (build 11.0.8+10-post-Ubuntu-0ubuntu120.04) OpenJDK 64-Bit Server VM (build 11.0.8+10-post-Ubuntu-0ubuntu120.04, mixed mode, sharing) $ sdk version SDKMAN 5.9.0+555
インストール
SDKMANを使ったインストールが可能なようなので、今回はこちらを使用します。
$ sdk install jbang
その他のインストール方法もあるようですが、使っている環境やツールが条件を満たさない場合は、バイナリをダウンロードして
インストールしましょう。
Releases · jbangdev/jbang · GitHub
今回のバージョンは、こちら。
$ jbang version 0.47.1
使う前に、BashのAuto Completionを有効にしておきましょう。
$ source <(jbang completion)
ヘルプ。
$ jbang -h jbang is a tool for building and running .java/.jsh scripts and jar packages. Usage: jbang [-h] [--verbose] [COMMAND] jbang init hello.java [args...] (to initialize a script) or jbang edit --live=code hello.java (to edit a script in IDE with live updates) or jbang hello.java [args...] (to run a .java file) or jbang gavsearch@jbangdev [args...] (to run a alias from a catalog) or jbang group-id:artifact-id:version [args...] (to run a .jar file found with a GAV id) -h, --help Display help/info --verbose jbang will be verbose on what it does. Essentials: run Builds and runs provided script. build Compiles and stores script in the cache. Editing: init Initialize a script. edit Setup a temporary project to edit script in an IDE. Caching: cache Manage compiled scripts in the local cache. jdk Manage Java Development Kits installed by jbang. Configuration: trust Manage which domains you trust to run scripts from. alias Manage aliases for scripts. catalog Manage Catalogs of aliases. Other: completion Output auto-completion script for bash/zsh. Usage: source <(jbang completion) version Display version info. wrapper Manage jbang wrapper for a folder. Copyright: 2020 Max Rydahl Andersen and jbang.dev contributors, License: MIT Website: https://jbang.dev
Javaプログラムを動かしてみる
まずは、軽く使ってみます。
最初の1文を見ると、スクリプトは1ファイルである必要がありそうですね。
A script is just a single .java file with a classic static main method or a .jsh file which will be passed to jshell.
複数のソースコードを扱うのは、実験的機能になっています。
Multiple source files (Experimental)
こんなJavaソースコードを用意。
HelloWorld.java
public class HelloWorld { public static void main(String... args) { String word; if (args.length == 0) { word = "World"; } else { word = args[0]; } System.out.printf("Hello %s!!%n", word); } }
このファイル、今ならjava
コマンドで実行できますけどね。
$ java HelloWorld.java Hello World!! $ java HelloWorld.java Scripting Hello Scripting!!
JBangで実行してみます。
$ jbang HelloWorld.java [jbang] Building jar... Hello World!!
なんか、JARファイルを作ってそうなログが出ています…。
2回目からは、出なくなりました。
$ jbang HelloWorld.java Hello World!!
引数もふつうに使えます。
$ jbang HelloWorld.java Scripting Hello Scripting!!
さて、いきなりビルドしている雰囲気があったのでどうなっているのか?ですが、Cachingのところを見るとよいようです。
デフォルトでは、$HOME/.jbang/cache
にいろいろできるそうな。ちょっと見てみましょう。
$ find ~/.jbang/cache -type f $HOME/.jbang/cache/jars/HelloWorld.java.a86770d39e60af63573a609bb669a610f5fa51605fb23be6383278d17f81423b/META-INF/maven/g/a/v/pom.xml $HOME/.jbang/cache/jars/HelloWorld.java.a86770d39e60af63573a609bb669a610f5fa51605fb23be6383278d17f81423b/HelloWorld.class $HOME/.jbang/cache/jars/HelloWorld.java.a86770d39e60af63573a609bb669a610f5fa51605fb23be6383278d17f81423b.jar
class
ファイルとか、JARファイルとかありますね…。
ここで、少しファイルを更新してみます。!!
を増やしてみました。
HelloWorld.java
public class HelloWorld { public static void main(String... args) { String word; if (args.length == 0) { word = "World"; } else { word = args[0]; } System.out.printf("Hello %s!!!!%n", word); } }
すると、再度ビルドされます。
$ jbang HelloWorld.java [jbang] Building jar... Hello World!!!!
JARファイル等自体が増えました。
$ find ~/.jbang/cache -type f $HOME/.jbang/cache/jars/HelloWorld.java.a86770d39e60af63573a609bb669a610f5fa51605fb23be6383278d17f81423b/META-INF/maven/g/a/v/pom.xml $HOME/.jbang/cache/jars/HelloWorld.java.a86770d39e60af63573a609bb669a610f5fa51605fb23be6383278d17f81423b/HelloWorld.class $HOME/.jbang/cache/jars/HelloWorld.java.a86770d39e60af63573a609bb669a610f5fa51605fb23be6383278d17f81423b.jar $HOME/.jbang/cache/jars/HelloWorld.java.319a2e49920a81a167dcf3c65b688c995dc8b2cb1cdfb52756e73335b035ead2.jar $HOME/.jbang/cache/jars/HelloWorld.java.319a2e49920a81a167dcf3c65b688c995dc8b2cb1cdfb52756e73335b035ead2/META-INF/maven/g/a/v/pom.xml $HOME/.jbang/cache/jars/HelloWorld.java.319a2e49920a81a167dcf3c65b688c995dc8b2cb1cdfb52756e73335b035ead2/HelloWorld.class
なんかハッシュ値っぽいものが見えますが、これはファイルのSHA256ですね。
$ sha256sum HelloWorld.java a86770d39e60af63573a609bb669a610f5fa51605fb23be6383278d17f81423b HelloWorld.java
これらのファイルは、jbang cache clear
でキャッシュクリアすることができます。
$ jbang cache clear [jbang] Clearing cache for urls [jbang] Clearing cache for jars [jbang] Clearing cache for scripts [jbang] Clearing cache for stdins
サブコマンドだけを指定すると、説明が見えるようです。jbang cache
$ jbang cache Missing required subcommand Usage: jbang cache [--verbose] [COMMAND] Manage compiled scripts in the local cache. --verbose jbang will be verbose on what it does. Commands: clear Clear cache of dependency list and temporary projects. By default this will clear the JAR, script, stdin and URL caches
キャッシュのディレクトリなどは、環境変数で設定できそうですね。
https://github.com/jbangdev/jbang/blob/v0.47.1/src/main/java/dev/jbang/Settings.java
なんか、脱線してきましたが、雰囲気が少しわかった気がします。
JShellで動かす
JBangは、スクリプトをJShellで動かす機能があります。.jsh
拡張子のファイルを与えると、JShellで実行しようとします。
試してみましょう。
HelloWorld.jsh
String word; if (args.length == 0) { word = "World"; } else { word = args[0]; } System.out.printf("Hello %s!!%n", word);
確認。
$ jbang HelloWorld.jsh JBang Hello JBang!! $ jbang HelloWorld.jsh Scripting Hello Scripting!!
実行できました。
--interactive
オプションを使うことで、インタラクティブに実行することもできるようですが、こちらの形態は
これくらいにしておきます。
実行可能ファイルとして扱う
スクリプトに対してShebangスタイル…の代わりに//
を使い、実行権限を与えることで、スクリプトを実行可能ファイルとして
動作させることができます。
こんな感じですね。
HelloWorld.java
//usr/bin/env jbang "$0" "$@" ; exit $? public class HelloWorld { public static void main(String... args) { String word; if (args.length == 0) { word = "World"; } else { word = args[0]; } System.out.printf("Hello %s!!%n", word); } }
実行権限を与えて。
$ chmod a+x HelloWorld.java
実行。
$ ./HelloWorld.java [jbang] Building jar... Hello World!!
こんな感じで、実行できますよ、と。これも、ここまでにしておきます。
依存関係を解決する
では、スクリプト内で依存関係を使用してみましょう。
ソースコード内に、//DEPS
と記述することで依存関係を解決してくれます。
//DEPS
で、間にスペースなどは入れてはいけません。
https://github.com/jbangdev/jbang/blob/v0.47.1/src/main/java/dev/jbang/Script.java#L30
こんな感じで、Commons Lang3を使用するソースコードを用意。
HelloWorldDeps.java
//DEPS org.apache.commons:commons-lang3:3.11 import org.apache.commons.lang3.StringUtils; public class HelloWorldDeps { public static void main(String... args) { String word; if (args.length == 0) { word = "World"; } else { word = args[0]; } System.out.println(StringUtils.join("Hello", " ", word, "!!")); } }
実行。
$ jbang HelloWorldDeps.java [jbang] [WARN] Detected missing dependencies in cache. [jbang] Resolving dependencies... [jbang] Resolving org.apache.commons:commons-lang3:3.11...Done [jbang] Dependencies resolved Hello World!! $ jbang HelloWorldDeps.java Scripting Hello Scripting!!
初回は、ビルドおよびライブラリのダウンロードが行われます。
キャッシュディレクトリの中身は、こんな感じになりました。
$ find ~/.jbang/cache -type f $HOME/.jbang/cache/jars/HelloWorldDeps.java.5f6cde3812d30c4eaecfe79d12e46f78dcd5ee9bf94768abeaea5aaaa171f5e5.jar $HOME/.jbang/cache/jars/HelloWorldDeps.java.5f6cde3812d30c4eaecfe79d12e46f78dcd5ee9bf94768abeaea5aaaa171f5e5/HelloWorldDeps.class $HOME/.jbang/cache/jars/HelloWorldDeps.java.5f6cde3812d30c4eaecfe79d12e46f78dcd5ee9bf94768abeaea5aaaa171f5e5/META-INF/maven/g/a/v/pom.xml
JBangは、Mavenリポジトリとしてはデフォルトでjcenterを使用するようです。
また、//REPOS
を使うことで他のリポジトリも利用することができるようです。
jcenterではなくMaven Centralがいいぞ、と思ったらこんな感じですかね。
//REPOS central=https://repo1.maven.org/maven2/ //DEPS org.apache.commons:commons-lang3:3.11
その他、Gitリポジトリを参照できるとか@Grab
形式(Grape)で書けるとかあるようですが、いいかなぁと。
依存関係解決の方法は?
ちょっと気になるのが、JBangの依存関係の解決方法です。
答えとしては、ShrinkWrapです。
https://github.com/jbangdev/jbang/blob/v0.47.1/src/main/java/dev/jbang/DependencyUtil.java
https://github.com/shrinkwrap/resolver/tree/3.1.4/maven
なので、依存するライブラリのダウンロード先はMavenのローカルリポジトリになりますし、settings.xml
も参照されます。
プロキシを設定するとしたら、settings.xml
になるんでしょうね。
JBangは、どのJavaで実行しているんだ?
これも、気になるところですよね。
--verbose
オプションを付けて実行すると、確認することができます。
$ jbang --verbose HelloWorld.java [jbang] System Java version detected as 11 [jbang] System Java version matches requested version 11 [jbang] Building jar... [jbang] compile: /usr/lib/jvm/default/bin/javac -d $HOME/.jbang/cache/jars/HelloWorld.java.c1a78dc36160dee8e968a9778b5fc17f8d74f633116e237206383be03aeeb42b HelloWorld.java [jbang] System Java version matches requested version 11 [jbang] run: /usr/lib/jvm/default/bin/java -classpath $HOME/.jbang/cache/jars/HelloWorld.java.c1a78dc36160dee8e968a9778b5fc17f8d74f633116e237206383be03aeeb42b.jar: HelloWorld Hello World!!
というわけで
いろいろ確認できた感じですね。
ちょっとしたところで使えたらいいかなーと。