steinybot
steinybot

Reputation: 6134

How to execute a java script with jshell?

Given that Java 9 is upon us and we can finally have a java REPL with jshell I was hoping there was a way to add a shebang to a script and have jshell interpret it.

I tried creating test.jsh:

#!/usr/bin/env jshell -s
System.out.println("Hello World")
/exit

However that gives:

⚡ ./test.jsh
|  Error:
|  illegal character: '#'
|  #!/usr/bin/env jshell -s
|  ^
|  Error:
|  illegal start of expression
|  #!/usr/bin/env jshell -s
|    ^
Hello World

It turns out there is an enhancement request for this in OpenJDK https://bugs.openjdk.java.net/browse/JDK-8167440.

Is there any other way to do this?

Upvotes: 34

Views: 10028

Answers (6)

user2609605
user2609605

Reputation: 618

Another variant for xxx.jsh:

#!/usr/bin/env -S java --source 17

import java.util.Arrays;

public class X {
  public static void main(String[] args) {
    System.out.println("Hello World with arguments "+Arrays.asList(args));
  }
}

Ensure to use extension jsp and to run env with option -S. Of course, java must be on the path.

Then you can invoke this script with 0, 1, 2, ... arguments and it prints back your argument list. I like this solution, because it spares jscript, except the extension in the file name.

Upvotes: 0

Mikko Ala-Fossi
Mikko Ala-Fossi

Reputation: 1

#!/usr/bin/awk NR>1{print|"jshell -"}
System.out.println("Hello world!");

This uses awk to skip the first line (shebang line) and pipe the rest to jshell.

Upvotes: 0

Adrian
Adrian

Reputation: 3711

The below works too; put it into a someScript.jsh file and run it with ./someScript.jsh. All arguments received by someScript.jsh will go to String[] args.

#!/home/gigi/.sdkman/candidates/java/current/bin/java --source 11

import java.util.Arrays;
import ro.go.adrhc.*; // example of using your classes, e.g. App below

public class X {
    public static void main(String[] args) {
        // do whatever you want here, e.g.:
        // System.out.println("Hello World");
        // or
        // use a class:
        // App.main(args);
        // e.g. from ro.go.adrhc package, by running:
        // CLASSPATH="/path-to-ro.go.adrhc-classes" ./someScript.jsh 
    }
}

The usage of the wrapping class, here X, is a mandatory trick for this to work. Use the Java version you have by changing /home/gigi/.sdkman/candidates/java/current/bin/java.
Inspired by https://blog.codefx.org/java/scripting-java-shebang/.

Upvotes: 5

Sormuras
Sormuras

Reputation: 9069

Use

//usr/bin/env jshell --show-version --execution local "$0" "$@"; exit $?

as the first line of test.jsh. The test.jsh script could look like:

//usr/bin/env jshell --show-version "$0" "$@"; exit $?
System.out.println("Hello World")
/exit

The command line option --show-version is optional, of course, but gives immediate feedback that the tool is running.

The extra command line option --execution local prevents jshell to spawn another VM. This speeds up launch time and if an exception is thrown by your script code, the local VM will exit.

Consult the output of jshell --help and jshell --help-extra for more options.

Update

Also take a look at https://github.com/jbangdev/jbang Having fun with Java scripting, which offers a neat wrapper around running .java files from the command line.

Upvotes: 43

Federico Fissore
Federico Fissore

Reputation: 872

Inspired by steiny answer, I came up with a more generic solution

https://gist.github.com/ffissore/012d7e32a096fde5266f49038c93dcaf

In essence: jshell-wrapper will strip the first line of the script (which is supposed to be the shebang) and will add a /exit at the end of the script

Upvotes: 2

steinybot
steinybot

Reputation: 6134

It turns out that with a bit of trickery there is a way, although I haven't fully managed to suppress the interpreted commands but pretty close to what I want.

Change test.jsh to:

#!/usr/bin/env sh
tail -n +4 "$0" | jshell -s "$@"
exit $?
System.out.println("Hello World")
/exit

Which gives us:

⚡ ./test.jsh
-> System.out.println("Hello World")
Hello World
-> /exit

Upvotes: 6

Related Questions