Globally Accessible Executables

Alejandro Belgrave
4 min readApr 10, 2019
gif of a person spinning a globe

Let’s imagine you just wrote an amazing script or CLI application. You cdinto that amazing app directory and then run python3 amazing_app.pyto begin the awesomeness. But what if you accidentally cd’d into the wrong directory, or a friend downloads your code and wants to access it from anywhere on their system? Luckily there is a way to setup your program as a global executable.

The Bin

gif of person jumping into a bin

It does not take a programmer a long time to find a project or framework that has a bin file or directory. Bin is short for binary, which is the machine level language of 1’s and 0’s that the computer understands. Since every language and program has to eventually convert to binary, all of the binary code (also known as executables) has conventionally been stored in bin.

Every console command is just a file on the system written in some programming language that gives instructions on how to respond to that command. That means that all the console is doing is finding the correct file given the command, and executing the code in that file.

Goal

So how do we turn ruby amazing_app.rb to amazing_app like we do with programs such as cd, ls, and rake? It happens in three steps:

  1. Writing a script that tells the console that it is a script and what programming language to use to interpret it
  2. Changing that file’s permissions so that the console is allowed to run it
  3. Adding the path of your program to the console’s list of paths

Let’s use a custom weather CLI I created in Ruby as an example. The goal is to get the app to run in the console typing tiempo —-ahora Bronx,US by following these three steps.

Step 1 — Shebang!

gif of woman shooting a cartoon gun that displays “bang”

The character sequence #! is used in UNIX based operating systems to indicate which interpreter to use to run the script. It is always the first line of the script. Common examples are:

#!/bin/bash for bash

#!/usr/bin/python for python

Or for our case using Ruby

/tiempo/bin/tiempo#!/usr/bin/env ruby

All this shebang line is, is a path to the interpreter the script intends to use. It always start with the #! and is followed by the absolute path to the interpreter.

Step 2 — RWX

person swinging an axe with the caption “I love my job”

If we try to run tiempo -h to get the help screen, it will throw an error about not having correct permissions. This is because by default any file you create does not have execution permissions, meaning it cannot be run directly by the operating system.

There are three types of permissions: Read, Write, and Execute. To check the permissions of a file or directory we can run ls -l <file_name>. There may be a lot of symbols and letters, but to make things simple:

  • r is for read
  • w is for write
  • and x is for execute.

And to change the permissions (or mode) we can use this handy command chmod, which ch(anges) the mod(e) of the file. In our case, all we need to run in our console

chmod +x bin/tiempo

This line adds execution permission to our file and now we can run tiempo -h from our directory in the console.

Step 3 — Back to the Bin

Up until now, this command will only work by calling the path to the bin directory. But, we want to be able to call it anywhere. To do this, we must abstract the path finding done by the operating system. $PATH is our answer. In UNIX based systems, there is an environment variable called $PATH that Is used to look up commands. To check your path, type echo $PATH. These are all the locations the operating system will look for commands in the console.

So, if we wanted to add a path to our specific program, all we have to do is run

#in terminal
export PATH=$PATH:/path/to/the/file

Note: The name of the command by default is the name of the file.

Now everything will work as expected from anywhere in the profile! The final issue occurs, however, when we restart. Since $PATH environment variable is created each time we begin a session and our changes aren’t saved in between session, the new path we added only exists until we close the console. To alleviate this, all we need to do add the path to the correct file when the shell launches. Since bash is our shell, we will add the following code to our .bashrc file in our home directory.

#~/.bashrcexport PATH=$PATH:/place/with/the/file

Note: This file is specific for the shell that you are using, and must be configured for every shell that is being used.

Wrap Up

Now everything works the way it is intended and we can check the weather from anywhere in our console. Many languages have libraries and frameworks that automates this task for you. For example, you do not need to do this process every time you install a new ruby gem or some python libraries. However, this is important to know for smaller scale testing and to understand the process in case you choose to automate this in the future.

Resources

https://www.computerhope.com/unix/uumask.htm#filepermissions

--

--

Alejandro Belgrave

Technologist specializing in Software Engineering. Diving deeper into how things work and why. Just sharing my questions and discoveries in real time.