Deck Out Your Mac Terminal: Bash Basics
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]
$
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
- Terminals Are Sexy: A curated list of Terminal frameworks, plugins & resources for CLI lovers.
- Take it up a notch with: How to Jazz Up Your Bash Terminal by rajaraodv
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 🤓.