jeval - command line Java code evaluator. It is similar to JShell except it provides you additional functionality and better integration with command-line. jeval allows you to declare dependencies in Java scripts to any libraries from Maven repository so that it will automatically resolve them and add to the Java script class path.
jeval allows you to execute Java code straight from the command line (same as you would do with perl -e, bash -c):
Hello world
In JShell to achieve that you need to use additional command like echo for example:
Additionally with jeval you can execute complete Java shell scripts and see compilation errors if any:
System.out.println("Hello world");
Hello world
With JShell you would have to use "/exit" in the end (otherwise it will start interactive mode):
System.out.println("Hello world"); /exit
Hello world
And JShell will not print you any errors:
System.out.println("Hello world"); error here /exit
Hello world
Another nice feature of jeval is that you can use pipes to pass data to your Java code (it binds all standard streams to support lazy reading from stdin predefined variable which is described later):
"ab,cd,ef"
For JShell there is no default support of that.
With jeval you can pass arguments to your Java scripts and handle them in same way as you do it in Java (through global args variable similar as in main() function):
"args arg1, arg2"
jeval does not require you to write class body with main method and all boilerplate code (but you still can do it if you want). You just write Java as you would do it with JShell.
You can download jeval from here
Java 17
Run following command if you use bash:
Or in case you use zsh:
Open cmd and execute following command:
jeval [ <JAVA_SCRIPT> | -e <JAVA_SNIPPET> | -i ] [ARGS]
Where:
Options:
To add new JAR files into class path use CLASSPATH env variable:
To pass arguments to the JVM use JAVA_ARGS env variable:
jeval provides you with some additional commands which you can put to your Java script files. All of them can be called from Java comments only. This helps to avoid adding any arbitrary changes to the Java language, which would prevent code from Java scripts, later be easily compilable with javac, or within IDEs.
This command allows you to include dependencies to any artifacts from Maven repository into the Java script files. Without need to create pom.xml or build.gradle for that.
Format of this command is:
//dependency ARTIFACT_NAME
Where ARTIFACT_NAME is a full name of the artifact in the format: <groupId>:<artifactId>[:<extension>[:<classifier>]]:<version>
Here is an example of simple script yaml.java which declares dependency on snakeyaml library and uses it:
//dependency org.yaml:snakeyaml:1.21 import org.yaml.snakeyaml.*; Yaml yaml = new Yaml(); String document = "\n- Hesperiidae\n- Papilionidae\n- Apatelodidae\n- Epiplemidae"; Listlist = (List ) yaml.load(document); System.out.println(list);
To run:
[Hesperiidae, Papilionidae, Apatelodidae, Epiplemidae]
To provide this functionality jeval integrates with depresolve tool which depends on maven-resolver library
jeval supports "//open" command similar to "/open" command which JShell provides. For example using this command you can extract some common logic into separate script file so jeval will read that.
For example here we keep parsing functions in script called parsers.java:
String parseToHex(int num) { return Integer.toHexString(num); }
And use them from script called script.java:
//open parsers.java printf(parseToHex(123) + "\n");
Now run:
7b
Here in script.java we include parsers.java and then call parseToHex method defined in parsers.java.
jeval by default imports following packages and static methods to the global space so you don't need to worry to import them each time manually:
java.util.stream.IntStream.* java.util.stream.Collectors.* java.lang.System.* java.nio.file.Files.* java.lang.Math.* java.util.Arrays.* javax.script.* java.lang.String.* java.util.* java.util.stream.* java.util.concurrent.* java.util.function.* java.util.regex.* java.io.* java.nio.* java.nio.file.* javax.xml.parsers.* javax.xml.xpath.* java.net.* java.net.http.* java.net.http.HttpResponse.* org.w3c.dom.* org.xml.sax.* java.time.* java.time.format.*
jeval comes with xfunction library and exports most of it classes and methods to global space as well.
String[] args
Arguments (if any) passed to the Java script
BufferedReader stdin
This is an instance of java.io.BufferedReader connected to System.in which allows you to operate with System.in as with stream of lines which is often useful when writing scripts (see Snippets section).
Optional<Path> scriptPath
Contains path to currently executing script. It is empty when used in Java snippets (jeval with -e option)
In addition to them jeval exports following variables from xfunction library:
void sleep(long msec)
Standard way to sleep in Java is pretty verbose because it throws checked exception which you need to handle:
try { Thread.sleep(1000); } catch (InterruptedException e) { throw new RuntimeException(e); }
When you write scripts in Java you probably want it to fit in one line and wrap any thrown exception to unchecked. With this function now you can sleep like that:
sleep(1000);
void error(String msg)
Throws RuntimeException with a given message
void isTrue(boolean expr)
If expr is false throw PreconditionException
void isTrue(boolean expr, String message)
If expr is false throw PreconditionException with message
Stream<String> findMatches(String regexp, String str)
Search string for substrings which satisfy the regexp and return them in a stream
Implements netcat operations:
static void listen(int port)
static void connect(String host, int port)
All input/output goes through stdin/stdout.
jeval supports "#!" interpreter directive which is available in Unix-like operating systems.
It allows instead of calling jeval each time to execute Java script:
To run it directly as ordinary executable file:
For that to work you need to put following line as a first line of script.java:
#!/usr/bin/env jeval
And make file executable:
Here are some examples of calling Java standard classes with jeval right from the command line.
Hello world
1 2 3 4 5 6 7 8 9
Tove
"1110"
/tmp/11873450107364399793tmp
"ab,cd,ef"
"args arg1, arg2"
[ggg1, ggg2]
1.jpg 3.jpg
Here is an example which creates 20 files with random names doing this in parallel on different threads:
With jeval we try not to modify/extend Java syntax for lambdas, variable type inference, method pointers or anything else like this is done in Beanshell. It means that your write plain Java code which later can be compiled with javac.
With -e option jeval evaluates the expression and prints its result. In this case method PrintStream::format returns reference to PrintStream so that is why it is printed.
To overcome this you can use printf method defined by jeval.
Those are generated by JShell. Most likely you run multiple threads and some of them try to execute unresolved snippets. Wait until jeval finishes execution then search for its diagnostics in stderr.
In JShell you cannot access classes from default (unnamed) packages.
Another issue is that Class.forName does not support classes which were declared inside JShell:
jshell> class X {} | created class X jshell> Class.forName("X") | Exception java.lang.ClassNotFoundException: X | at URLClassLoader.findClass (URLClassLoader.java:471) | at DefaultLoaderDelegate$RemoteClassLoader.findClass (DefaultLoaderDelegate.java:154) | at ClassLoader.loadClass (ClassLoader.java:588) | at ClassLoader.loadClass (ClassLoader.java:521) | at Class.forName0 (Native Method) | at Class.forName (Class.java:315) | at (#4:1)
Since jeval depends on JShell, same constraints applies to it.