Reputation: 6134
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
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
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
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
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
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
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