Globally Accessible Executables
Let’s imagine you just wrote an amazing script or CLI application. You cd
into that amazing app
directory and then run python3 amazing_app.py
to 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
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:
- Writing a script that tells the console that it is a script and what programming language to use to interpret it
- Changing that file’s permissions so that the console is allowed to run it
- 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!
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
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