6 things you can do with JBang but you can’t with Shell

Using Java as scripting language has become a popular option in the last few years thanks to the JShell tool. In this article we will learn how the JBang scripting tool can take your Java scripting power at another level.

Automatic fetching of dependencies

The most impressing feature of JBang is dependency management. You can run a Java application as a script and configure dependencies in multiple ways. The simplest one is to specify your dependencies in your Java class using a Comment notation:

///usr/bin/env jbang "$0" "$@" ; exit $?
//DEPS io.quarkus:quarkus-resteasy:2.7.2.Final

import io.quarkus.runtime.Quarkus;
import javax.enterprise.context.ApplicationScoped;
import javax.ws.rs.GET;
import javax.ws.rs.Path;

@Path("/hello")
@ApplicationScoped
public class quarkus {

    @GET
    public String sayHello() {
        return "hello from Quarkus with jbang.dev";
    }

}

If you don’t want to hard code the dependencies in your Class, you can use the –deps command line option. For example, you can run the above example also with:

jbang --deps=io.quarkus:quarkus-resteasy:2.7.2.Final quarkus.java

Using Command Line Options

JBangs allows to provide command line options to your script. When you use this feature in combination with the CommandLine template the result is amazing. An example is worth 1000 words:

import picocli.CommandLine;
import io.quarkus.runtime.annotations.QuarkusMain;
import io.quarkus.runtime.QuarkusApplication;
import java.sql.*;

@CommandLine.Command
public class jdbc implements Runnable {

    @CommandLine.Option(names = {"-q", "--query"}, description = "SQL Query", defaultValue = "select now()")
    String QUERY;

    @CommandLine.Option(names = {"-url"}, description = "JDBC URL", defaultValue = "jdbc:postgresql://localhost:5432/postgres")
    String DB_URL;

    @CommandLine.Option(names = {"-u", "--user"}, description = "username", defaultValue = "postgres")
    String USER;

    @CommandLine.Option(names = {"-p", "--password"}, description = "password", defaultValue = "postgres")
    String PASS;

    @Override
    public void run() {

        try(Connection conn = DriverManager.getConnection(DB_URL, USER, PASS);
            Statement stmt = conn.createStatement();
            ResultSet rs = stmt.executeQuery(QUERY);) {
            // Extract data from result set
            ResultSetMetaData meta = rs.getMetaData();
            int colCount = meta.getColumnCount();
            while (rs.next())
            {
                for (int col=1; col <= colCount; col++)
                {
                    Object value = rs.getObject(col);
                    if (value != null)
                    {
                        System.out.print(value.toString());
                    }
                }
            }

        } catch (SQLException e) {
            e.printStackTrace();
        }
    }

}

So, the above example uses the Pico CLI template and contains multiple CommandLine.Option. Look at what you can do with it:

jbang --deps=org.postgresql:postgresql:42.3.3 jdbc.java --query="select now()" --user=postgres --password=postgres

In a nutshell, you can connect to any Database through a JDBC Driver and run a SQL Statement. You will pass the connection parameters as command line options.

jbang vs jshell

Run scripts from remote sources

Another cool feature is the ability to run JBang Class files from remote (trusted sources). For example, we will execute the hello.java Class from JBang catalog

jbang https://github.com/jbangdev/jbang-catalog/blob/master/hello.java Frank

Hello Frank

You can even use an alias to reference the remote repository. For example, to reference the hello.java which is in jbang-catalog repository of GitHub jbangdev user, you can simply use the jbangdev alias:

jbang hello@jbangdev

Execute Java code snippets from the shell

JBang lets you execute Java code snippets at your fingertips with the -c options. Here’s an example to generate a random String:

jbang -c 'System.out.println(java.util.UUID.randomUUID())'
efa845ef-48c7-467d-a6b7-2994900d403f

On the other hand, you can pipe the result of a shell command into JBang. In this case, you will get as input the stream of characters through the Java String lines() method. Then, you can use the Java Stream API to elaborate on your lines of text. For example, to uppercase the content of a file you can use:

cat hello.txt | jbang -c 'lines().forEach(line -> System.out.println(line.toUpperCase()))'
HELLO WORLD
JBANG!

Manage JDK from JBang

Do you need a quick way to install Java? or force using a specific Java version? That’s a piece of cake.

To install Java 17, that’s enough:

jbang jdk install 17

On the other hand, to check the list of JDK installed:

jbang jdk list

Finally, to force JBang to use a specific Java version, use the //JAVA comment at the top of it:

///usr/bin/env jbang "$0" "$@" ; exit $?
//JAVA 14

class helloworld {

    public static void main(String[] args) {
        if(args.length==0) {
            System.out.println("Hello World!");
        } else {
            System.out.println("Hello " + args[0]);
              
        }
    }
}

Aliases

To simplify the execution of JBang scripts, yo can add aliases to them. These aliases are stored locally in a jbang-catalog.json file, and can be as well accessed from everywhere.

For example, supposing you were to execute the following remote script:

jbang https://github.com/jbangdev/jbang-catalog/blob/main/hello.java Hey
Hello Hey

You can create an alias to that script using the “jbang alias” command. For example:

jbang alias add -f . https://github.com/jbangdev/jbang-catalog/blob/main/hello.java

[jbang] Alias 'hello' added to './jbang-catalog.json'

From now on, you can refer to that alias to run the hello.java script:

jbang hello Hey!
Hello Hey!

In the above example, we have stored the alias locally. JBang can also use JBang aliases available on GitHub in JBang Catalogs

As an example, check this article to learn how to run the Camel JBang App: How to run JBangs apps from the Catalog.