Deck Out Your Mac Terminal: Bash Basics

Zarin Lokhandwala
9 min readDec 16, 2018

--

As a dev, my first instinct, to do even the most menial task such as opening a file, is…

⌘ + Space + “ter”

Xcode CLI Tools

In order to install many common Unix-based tools, you will require the GCC compiler included in Xcode CLI tools.

To install it, run the following in your terminal:

$ xcode-select --install

Homebrew and Cask

Homebrew

Now, as promised, we are going to turn your CLI into a powerful resource, and that wouldn’t be complete without “the missing package manager for macOS”: Homebrew 🍺.

What exactly is a package manager? It automates the installation and maintenance of software libraries. Homebrew installs all software to /usr/local/Cellar, which creates symbolic links in /usr/local/bin and /usr/local/lib that point back to Cellar. In this fashion, Homebrew can easily manage packages and enforce an existing best practice.

Note: Homebrew is a general purpose package manager for software such as Git, Python or MongoDB. It is NOT meant to install packages whose languages already maintain their own package managers. For instance, use pip to install packages for Python, not Homebrew.

Install Homebrew by running:

$ /usr/bin/ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)"

You can reference the official instructions here.

Cask

Cask, is an extension of Homebrew. It’s used to install macOS native apps such as Docker, Java, Alfred, and even Google Chrome (i.e. graphical apps). No need for downloading .dmg files and dragging them into your Applications folder.

To use Cask to install an application, run:

$ brew install --cask <app_name>

For instance, let’s say we wanted to install Atom, not that I would, but nevertheless…

$ brew install --cask atom 
Updating Homebrew...
==> Auto-updated Homebrew!
Updated 3 taps (homebrew/cask-versions, homebrew/core and homebrew/cask).
==> Updated Formulae
alexjs certbot docker-machine-nfs fn gitlab-runner libatomic_ops nmh opa
bitrise cockroach erlang frugal grafana libtins node osmium-tool
blink1 convox exploitdb fx hub media-info oniguruma rustup-init
==> Satisfying dependencies
==> Downloading https://github.com/atom/atom/releases/download/v1.33.0/atom-mac.zip
==> Downloading from https://github-production-release-asset-2e65be.s3.amazonaws.com/3228505/9fb03c00-f2f0-11e8-9977-ad85bf1925f1?X-Amz-Algorithm=AWS4-HMAC-SHA256&
######################################################################## 100.0%
==> Verifying SHA-256 checksum for Cask 'atom'.
==> Installing Cask atom
==> Moving App 'Atom.app' to '/Applications/Atom.app'.
==> Linking Binary 'apm' to '/usr/local/bin/apm'.
==> Linking Binary 'atom.sh' to '/usr/local/bin/atom'.
🍺 atom was successfully installed!

Common Commands and Packages

To install a package or Formula (Homebrew terminology), run:

$ brew install <formula>

To update Homebrew:

$ brew update

To update Homebrew manually:

$ cd /usr/local/Homebrew/
$ git fetch origin
$ git reset --hard origin/master

To see which formulas:versions you have already installed via brew:

$ brew list --versions

To see which packages you have installed via cask:

$ brew search --casks

To search for available packages:

$ brew search

To see which formulas are outdated:

$ brew outdated

To update a formula:

$ brew upgrade <formula>

To remove older versions of formulas:

# to view which formulas homebrew would remove without deleting them
brew cleanup --dry-run
# to actually remove older versions
brew cleanup

To view more information about a formula:

$ brew info <formula>

To uninstall a formula:

$ brew uninstall <formula>

Common packages installed via brew:

  • git
  • maven
  • nvm (node version manager is used to install nodejs)
  • wget
  • bash-completion

Common packages installed via brew cask:

  • sublime
  • virtualbox
  • java
  • docker

Bash Profile

Now, let’s dive right into how you go from the normal boring bash shell (left) to the customized and colorized 🌈 bash shell (right).

If you aren’t familiar with how Bash profiles work, then you can read the Bash Rundown section below to get a better understanding. For those that have a decent amount of understanding, just follow the steps below!

Let’s start by creating a .bash_profile file in your user home directory:

$ touch ~/.bash_profile

In order for the login shell to execute your .bash_profile you must make this file executable. Run the chmod command to change the access permissions for the user (u) that owns the file to be able to execute (x) it.

$ chmod u+x ~/.bash_profile

Now, in Bash, you have an inbuilt shell variable called PS1, otherwise known as Prompt String 1. This is the primary prompt which is displayed before each command and therefore, the one that we will customize.

Bash is not very user-friendly at times, so to help us generate our prompt, let’s use an online drag-and-drop tool: bashrcgenerator. I’m going to show you how to enable the prompt seen below, but feel free to design your own!

07:23 AM [username@mycomputername:/present/working/directory]
$
output of PS1 using the online tool: bashrcgenerator

Add the generated content from the website to your .bash_profile. You will notice that at the very top of the file there is an expression #!/bin/bash. The #! syntax is referred to as Shebang and is used in scripts to indicate an interpreter for execution under UNIX/Linux OS. The/bin/bash lets the interpreter know which shell to use while executing the script.

#!/bin/bash# set prompt
export PS1="\@ [\u@\h:\w]\n\\$ \[$(tput sgr0)\]"

Now let’s colorize our prompt. First, we will add custom color configurations. You don’t have to use custom colors, but I like them, so here you go.

#!/bin/bash# set custom colors
export COLOR_NC='\e[0m' # No Color
export COLOR_WHITE='\e[1;37m'
export COLOR_BLACK='\e[0;30m'
export COLOR_BLUE='\e[0;34m'
export COLOR_LIGHT_BLUE='\e[1;34m'
export COLOR_GREEN='\e[0;32m'
export COLOR_LIGHT_GREEN='\e[1;32m'
export COLOR_CYAN='\e[0;36m'
export COLOR_LIGHT_CYAN='\e[1;36m'
export COLOR_RED='\e[0;31m'
export COLOR_LIGHT_RED='\e[1;31m'
export COLOR_PURPLE='\e[0;35m'
export COLOR_LIGHT_PURPLE='\e[1;35m'
export COLOR_BROWN='\e[0;33m'
export COLOR_YELLOW='\e[1;33m'
export COLOR_GRAY='\e[1;30m'
export COLOR_LIGHT_GRAY='\e[0;37m'
# set prompt
export PS1="\@ [\u@\h:\w]\n\\$ \[$(tput sgr0)\]"

Next, apply these colors to PS1. In order to apply these colors to each item in your custom prompt, the color must be placed at the start of the character you want to apply the color to. Until another color is introduced in the lineup, the initial color will be applied to each and every character afterwards.

Let’s take a look at how to apply colors now:

#!/bin/bash# set custom colors
...
# set prompt
export PS1="$COLOR_LIGHT_GREEN\@ $COLOR_WHITE[$COLOR_CYAN\u@\h$COLOR_WHITE:$COLOR_PURPLE\w$COLOR_WHITE]\n\\$ \[$(tput sgr0)\]"

🚨Lastly, don’t forget to source your .bash_profile. The source command reads and executes the commands in the input file. You must do this every time you want to enable the changes you made to your profile. Execute the following:

$ source ~/.bash_profile

You should now see your new and pretty prompt!

Bash Rundown

Overview

The Bourne shell, better known as bash, is the shell made for UNIX and Linux environments, and happens to be the default shell for macOS. In Bash, there are two config files: .bash_profile and .bashrc. You can place your custom configurations in either file, and can create either if it doesn’t exist. However, why are there two files? Are they actually different?

The .bash_profile is executed for login shells, while the .bashrc is executed for interactive non-login shells.

Login vs Non-login Shell

In the *nix (UNIX + Linux) environment, a login shell is the first process that is executed under your username when you login, either directly through a machine or remotely via ssh, for an interactive session. Login shells typically read files that are used to configure your shell such as setting environment variables. In the case of the Bourne shell, the .bash_profile is executed to configure your shell before the initial command prompt appears on the terminal.

However, if you’re already logged into your machine and open up a new terminal window, then the .bashrc is executed.

Why the need for two files?

As mentioned before, in the *nix environment, if there is some diagnostic info that you wish to collect regarding your machine, such as memory usage, and only want to see this on the login, you should ONLY place that information in the ~/.bash_profile space.

Mac OS X — the exception

Your macOS is the exception to the rule and runs a login shell by default for each new terminal window, which calls the .bash_profile instead of the .bashrc.

For those decorating their terminals, not on macOS, take a look at the two files here!

What else to add to your Bash Profile?

PATH Variable

PATH is an environment variable, which contains a list of file system paths where the OS can find programs to run. When you run a program in bash, the OS sequentially looks for the program in each of the paths contained in the PATH, and will run the first instance of the program that it finds. If the program could not be found in any of those paths, then your Terminal returns with a program not found error.

To check what your default PATH is set to, run the following:

$ echo $PATH

The first path should be/usr/local/bin followed by the second path of /usr/bin. All programs that are local to your use of the OS are not managed by a distribution package manager (i.e. locally compiled packages) are located in /usr/local/bin. All programs that need to be globally accessible by other users are stored in /usr/bin. You should not install locally compiled packages to /usr/bin because future distribution upgrades may modify or delete those packages without any warning. Installing packages in this manner is best practice and highly encouraged to be followed.

If that isn’t what your PATH variable looks like, then you must correct this in your .bash_profile by adding this to your file:

export PATH=/usr/local/bin:$PATH

This simply appends the /usr/local/bin path to the front of your preexisting PATH variable. Each path is delimited by a colon (:). Don’t forget to source the file!

Other Variables

There will be programs that require you to set a specific variable. One of those programs is Java. For instance, let’s say I installed JDK 10.0.1. In order to set the JAVA_HOME environment variable, I would add either option 1 or 2 from the following lines to my .bash_profile:

# ===== OPTION 1 =====# set JAVA_HOME explicitly 
export JAVA_HOME=/Library/Java/JavaVirtualMachines/jdk-10.0.1.jdk/Contents/Home
# append JAVA_HOME to the end of the existing PATH variable
export PATH=$PATH:$JAVA_HOME
# ===== OPTION 2 =====# set JAVA_HOME via environment variables
export JAVA_HOME=$(/usr/libexec/java_home -v 10.0.1)
# append JAVA_HOME to the end of the existing PATH variable
export PATH=$PATH:$JAVA_HOME

Aliases

In Bash, you are allowed to create your own shortcuts and time-savers through the use of aliases and shell functions. Reference this simple guide for a better understanding.

Personally, I don’t find the need to create many aliases, but there is one alias that I use to help colorize my search results while searching (i.e. grep) for keywords in my filesystem. Add the following to your .bash_profile and source it to enable the change.

# colorize the grep command
alias grep='grep --color=auto'

Now if I were to search for files beginning with the phrase “.bash”, my terminal would display the following:

Add Ons

If you have Sublime installed, which you should, then you can open up directories and files straight from your terminal with Sublime.

Read this short 2 min guide on how to do just that!

Recap

By now, you would have done the following:

  • Installed: Xcode Command Line Tools, Homebrew + Cask
  • Customized your prompt
  • Setup your path variable, set other variables, set aliases

References

If you want to switch from Bash to a fancy shell called Z shell, read the next part of this post!

That’s all for now 🎉! I hope you enjoyed this walkthrough and learn to love your terminal the same way I do 🤓.

--

--