1

I'm creating a CLI program in Kotlin (Java). I want to bind the main function/class to an individual command, such as program.

However, from what I searched online, it seems like the only way to run a Java program is with the command java. For example, java -jar program.jar args, or java -cp "..." Program args. But they are very inconvenient for users to type every time, which I experienced when I used BFG, a command-line repository cleaner tool written in Java.

I could use an alias, but there is no standard way to add aliases to a system when users install my CLI program. For example, most people use Bash, so I have to install the alias to .bashrc or .profile, but others might use zsh or csh, which don't read .profile.

I could also wrap it with a native program, but I'll need to write that in a native language just to redirect the commands, which I might as well just rewrite the entire thing in that language.

In Node.js, developers can simply specify their command in their package.json and everyone who installed this package through npm i -g can use the command. What is the simplest alternative to this in JVM languages?

3
  • 3
    Just create the script, wrap your java call inside, and pass arguments if there are any. Then, make sure to put your script on PATH. It should do. Commented Jul 6, 2020 at 15:52
  • @Oo.oO Then the command would have to be something like program.sh? That's fine, and in my installer, should I put the script along with my .jar in a universal /bin folder like /usr/local/bin? or should I leave them where they are and modify $PATH? Commented Jul 6, 2020 at 16:01
  • 1
    If you're building your project with Gradle, the easiest way is to use the application plugin. Commented Jul 6, 2020 at 16:25

1 Answer 1

3

If I were you, I'd have organised things following way:

your_app
|-- Hello.java
|-- bin
|   `-- hello.sh
`-- lib
    `-- hello.jar

Inside Hello.java there is a simple code:

public class Hello {
  public static void main(String [] arg) {
    if(arg.length > 0) {
      System.out.println("Hello: " + arg[0]);
    }
  }
}

and you can build it like this:

> javac Hello.java
> jar cf lib/hello.jar Hello.class

Note that for simplicity I do it ugly way. You should definitely look at something called Manifest - https://docs.oracle.com/javase/tutorial/deployment/jar/manifestindex.html

Then, inside hello.sh you can put something similar to:

#!/bin/bash

export HELLO_LOCATION=${HELLO_LOCATION:-$(cd $(dirname $0)/.. ; pwd)

java -cp $HELLO_LOCATION/lib/hello.jar Hello $*

Once everything is in place, you are free to put your_app anywhere in the file system. All you have to do (to get it visible), is to put hello.sh on PATH. So, you tell people to do:

export PATH=${SOME_LOCATION}/your_app/bin:${PATH}

Since now, everybody can do:

> hello.sh hello
Hello: hello

You may also (for the convenience) create symbolic link inside /usr/bin or /usr/local/bin.

If you plan to release multiple version of your code, it's worth looking at something called: modules.

You can read about them here: https://www.owsiak.org/modules-as-a-convenient-way-of-choosing-build-chain-on-macos/

Sample targets macOS but will do for Linux as well.

Sign up to request clarification or add additional context in comments.

3 Comments

Mmm, that's probably the standard approach. But there's no need for the .sh extension; most installed commands don't use it. Remember that shells use the first line of a file, not its filename, to work out how to execute it. (Many programs you've installed via a package manager, or even part of the system, are actually driver scripts rather than machine-code executables!)
That's true. .sh can be omitted. I, personally, prefer to keep .sh just to make user aware of the fact that he/she is actually running shell script. But that's 100% true it's not required.
One reason not to use .sh is that you might want to change the driver script into a machine-code executable, or some other type of file. And if there were a reason why the user needed to know what type of file they were running, more files would use one… For example, /usr/bin on this Mac turns out to have about 600 Mach-O executables, 70 shell scripts, 160 Perl scripts, and a handful of Python, Ruby, PHP, and dtrace — very few of which have any extension!

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.