Executable Bash Files on MacOS

Griffin Poole
The Startup
Published in
6 min readJun 28, 2020

Control your “.commands”!

Bash, the default shell for most MacOS versions and Linux Distributions.

Ever since I began my development journey, I’ve dreamed of my first completion of a fully installable Desktop application. The ability to code a project, compile it down to its runnable code, and develop an installer to automate setup on supported operating systems; these steps have always struck me as a culmination of software engineering skill.

Today is not the day that I developed my first fully-installable application.

Today is, however, a day that I have gotten one step closer. Over time, as I progressed along my coding track, I have slowly been picking up tools and tricks of the UNIX/Bash Shell language. The UNIX shell is essentially a command-line-interface to engage with the operating systems features, and in MacOS’s case, it provides us a window to engage our shell through the built-in Terminal application.

The classic Mac Terminal window.

Bash provides much closer interaction with the Operating System, and supports a variety of tools for developers and computer nerds alike. Understanding Bash (and Windows shell of course) is an extremely powerful tool for us techies, often being the simplest way to install libraries/frameworks, run compilers, execute code and traverse our computer’s file system. In the past, I have briefly mentioned some of the things you can do with Bash, but in short, an understanding of Bash can save you a lot of time in your personal work (aliasing and functions for shortcuts) and can help you develop projects usable by non-developers(installers, executables, etc.).

In this case, I was developing a small applet for my partner to help her manage her tax allocations at a new job. The app was fairly simple. It was mostly a calculator for deciding how much of a paycheck needs to be saved for taxes and a table to display old paycheck entries:

The application was written in the IntelliJ IDEA environment, and utilized the JavaFX library for its GUI. I have done a write up on the JavaFX library before, but all you need to know is that it is a GUI support package that used to be included with the JDK, but is no longer supported by default.

Now I WAS just going to include JavaFX as a dependency library in my IntelliJ setup, build a JAR artifact, and send that over as the app. JAR files are executable on Mac and Windows systems with Java installed, and having all the compiled code in a simple clickable file was all I needed for the project.

The problem: JavaFX was no longer included in the JDK, and IntelliJ (at the time I’m writing this) has a known issue of JAR artifacts not being buildable for JavaFX apps due to a missing -fx:build in the JDK. Instead, we have to include the JavaFX library as a module dependency and build a normal JAR artifact. Then it works, right?!

The dreaded runtime environment error

Big nope on that. Essentially, the JVM is missing the relevant JavaFX modules during runtime to execute the code that we require in our compiled project. It took me a while to figure out the solution to this, but I found a helpful stack overflow solution here.

Long story short, I could run the JAR through a Bash command, include the modules’ file location, and list the modules that I needed to import from it in the execute command.

java --module-path /Users/griffinpoole/Desktop/appTest/javafx-sdk-11.0.2/lib/ --add-modules javafx.controls,javafx.fxml -jar JuliaApplet.jar

To be clear, the code above is one line, and what we’re doing is specifying the path to the JavaFX module library that I’ve installed into the projects folder, then specifying that we need to import the .controls and .fxml modules, and finally running the JuliaApplet.jar file. Thanks, Bash!

But this is far from simple, and far from clickable. I still wanted to avoid having to put my partner through learning how to open up a Terminal, cd to a directory, and copy in a scary and complicated command. So I did some research, and cue the Bash executable.

A text file with Bash commands being executed by a double-click.

It is possible to write a text file containing Bash commands where each new line is a separate command to be executed in top-down order. The specific commands can be any valid Bash call, and there can be as many commands as you can fit in a valid file. Below I’ve copied the same three lines of code above, and listed what they do:

cd ~/Desktop/appTestjava --module-path javafx-sdk-11.0.2/lib/ --add-modules javafx.controls,javafx.fxml -jar JuliaApplet.jarexit 0
  1. cd the new shell into the project directory for easy file addresses
  2. Run the JAR file pointing to the JavaFX library and importing modules
  3. Gracefully exit the shell

So we can make a new text file and edit its contents to match the code above (P.S. If you append .command to the file’s name, it is automatically recognized as a shell file, but you still need to set executable permissions like we’ll do later).

The new text file and its contents.

Once we have our code in the file, we need to make it executable by updating its access permission. In UNIX shell systems, access permissions are used to control who is allowed to interact with system files and in what way. They are specified by two attributes: user classes, and permission modes. The different user classes are:

  1. Users — The owner of the file
  2. Group — Members of the files group
  3. Other — Users that are not the file’s owner, and not a member of the group

These cover all possible users who might want to access a file on a given system. Every file has access permission modes for each user class, and they can be toggled on and off depending on the system’s needs. The most important of the permission modes for our purposes are:

  1. read — Can view the contents of a file
  2. write — Can change the contents of a file
  3. execute — Can execute a file

The UNIX command to update file permissions is chmod, and we want to change our text files permissions so that all users can execute the file. The resulting Bash command to set the file’s properties is:

chmod a+x run
Updating the files permission to be executable.

Success! Just like that we’ve worked around a complicated bug from version updates by hardwiring into our terminal and setting up the environment ourselves. We were even able to make it easy enough for non-coders to use. Is it pretty? No. Is it clean? Probably not. Is it professional? Almost definitely not. Does it work? You bet it does.

Aside from being useful for workarounds like this, Bash executables can be great for shortcuts and toolkits on a wide variety of projects. In the past for example (to streamline the opening of multi-sided projects), I’ve created Bash aliases that open up multiple terminal windows, cd the shells into various project directories, open up files, boot up development servers and so on all in one step. The same processes can be separated into a Bash executable file, so that all the setup to resume work on a complicated project can be started with a double-click.

I hope you’ve found this article useful. I, for one, love finding new and creative ways to streamline my work process, and I hope I’ve inspired some of you to explore more of the capabilities of the Bash Shell. Thanks for reading!

--

--

Griffin Poole
The Startup

Software Engineer, Web Developer, Neuroscience BA