No matter what your daily driver is, most of us also have to write some shell scripts to automate stuff. Usually, we would use bash script, Python, Perl, or some other scripting language. But what if we could use a compiled language instead?
One of the significant advantages of shell scripts is that they are just a single file that you can execute easily. Trying to get the same easy usage from a compiled language, I started using Go, which compiles into a single executable that’s runnable without any additional framework or setup on any machine (that is was compiled for). But Java is my daily driver at work, and I’m the only one at the office trying to learn Go, it might not be the best choice.
After reading “Java for Everything”, I asked myself: “Why not use Java?”.
Java for CLI
Java wouldn’t be the best choice for CLI compared to many other options.
Handling files wasn’t easy/fun until Java 7, no
sed, etc., no good/up-to-date
curses library available, input handling wasn’t fun either, and no helpful built-in arguments library.
Why would I choose this language for my shell scripts?
Well, because it’s my daily driver, I’m fluent in it, and it has all these great advantages in other use cases: a mature, strong-typed language with a humongous amount of 3rd-party-libraries for everything you might need.
Jar + Shell Script = Single Executable
The first idea was to build a Jar-file, put it somewhere safe and call it via a shell script. It’s simple, but you need to maintain two files. There has to be a better way to end up with a single file.
Then, I discovered that Java actually supports shebangs. And you can put the Jar in the shell script itself:
We just created a strange hybrid: A script containing an ASCII shebang followed by binary data from the Jar. If we edit that file, we’ll kill its encoding, so don’t edit it manually.
At first, I wasn’t sure why and how this was actually working.
It’s really simple: The program loader seems to take the shebang and passes the rest of the file to the correct executable. Jar files are just Zip files, and their headers are at the end of the file, so the shebang isn’t interfering.
Making it easier
Thanks to Gradle, we can automate the building of the shell script:
So now we just have to run
./gradlew shellScript and a single file executable will be built for us!
As mentioned above, the file contains plain text and binary data, which a text editor cannot handle and will break your file if you open and save it.
Another caveat is a pretty obvious one: compiled code is unreadable code. With a bash script/Python/Perl/etc., you can always read and easily edit the script.
- It’s great to build small tools with your preferred language instead of bash script.
- You can easily share your own Java code and libraries between different shell scripts.
- Developers without bash knowledge can contribute.
- Having it contained in a single file makes it easier to distribute.