Shell and Terminal on Mac

Abbreviated Fundamentals of the Command Line Interface

What’s what

Most users of personal computers today are intimately familiar with the graphical user interface (GUI) where interactions with the computer typically involve using a mouse (and sometimes the keyboard) to click and navigate through windows, icons, menus, and buttons.

Before GUIs became common, computers were often operated through a text-based method called a command-line interface (CLI). And this approach is sill widely used today.

In the “old days,” most computers would boot into the CLI because there was no graphical altnernative. Today, every major operating system still includes a way to access the command line through an application called a terminal emulator (or just terminal for short). On macOS, the app is unceremoniously named “Terminal” and can be found in the /Applications/Utilities directory. But you can download other terminal applications, including iTerm2, Warp, Alacritty, Hyper, Tabby, Kitty, Rio, or others.

Every terminal application has a text-based input field called the command line that begins with a prompt (usually > or $). When you type and submit a command, the Shell Command Line Interpreter (or just Shell) parses and interprets the text you have typed, carrying out your instructions and possibly returning information to you in the terminal. This Read-Eval-Print loop (REPL) is common when computer programs allow “live” interaction between the user and computer.

The default shell on macOS at the time of writing is zsh (pronounced the “Z shell”) which replaced bash (the “Bourne Again SHell”) as the macOS default starting with the release of macOS Catalina in 2019. bash, as evidenced by its name, was an evolution of the Bourne shell (with the short name sh). To round out the list, the only other popular shell today is fish (the “friendly” shell).

Shell Basics

First, let’s determine what shell you are using. Open your terminal application and type echo $SHELL at the command prompt. If the response is /bin/zsh then its zsh.

Most commands tend to have the following structure

[command] [options] [arguments]

You’ll need help. Often. The commands run-help and man (short for manual) following by a command name will bring up the command’s documentation using the terminal pager less. For navigation, arrow keys move one line at a time, page up/down and the spacebar move one page at a time, and q exits. For more succinct documentation, visit https://tldr.sh/ in a browser, which can be used interactively or downloaded.

Moving around

Navigating the hierarchy of directories and performing simple operations on files are fundamental to working from the command line. Note that many of the following tools take the verbose -v option, which prints a confirmation message in the terminal and is useful to “see” what these commands do, especially while learning them.

  • pwd prints the current (working) directory
  • cd changes the working directory
  • ls list the contents of a directory
  • .. denote the parent directory
  • . denotes the current directory (useful, eg, when copying)

Creating, moving, copying, and deleting files and directories are common operations:

  • touch updates the access and modification times of files, but also creates a file if it does not yet exist
  • mv move (or rename) one or more files or directories
  • cp copy one or more files or directories
  • rm remove (ie, permanently delete) file(s). A good alternative is to “trash” them with mv file_name ~/.Trash
  • mkdir create a new directory
  • rmdir removes directories (use with caution!) A good alternative is to “trash” them by installing the trash command with brew install trash and then running trash dir_name

It is often useful to use wildcards to return a “group” of files. Common wildcards include:

  • * denotes zero or more characters
  • ? a single character
  • [abc] a list of permited characters
  • [a-z] a set of characters

Editing Text at the Command Line

When working in a terminal, you’ll often need to edit what you’ve typed, e.g., to fix a typo, move back a few words, delete a chunk of text, or recall and modify a previous command. While you can do this with arrow keys and backspace, you’ll become dramatically faster once you learn a handful of editing shortcuts.

Most shells (including zsh and bash) provide “line editing,” meaning you can edit the current command before you run it. These editing features come from a library called readline (used by bash) or a similar system called zle (the Zsh Line Editor). Both support two classic styles of keybindings: Emacs-style editing (the default in most shells) and Vi-style editing (popular among Vim users). These styles trace back to two influential text editors from the 1970s: Emacs and vi, which shaped how programmers interact with text even to this day.

Emacs-style keybindings (default)

In Emacs mode, you use Control key combinations to move and edit efficiently. These are widely supported across shells and many terminal programs. Here are the most useful ones to memorize. Note that on the macOS Terminal, “Alt” is often the Option key.

  • Ctrl-A jump to the beginning of the line
  • Ctrl-E jump to the end of the line
  • Ctrl-B move back one letter
  • Ctrl-F move forward one letter
  • Alt-B or Esc-B move back one word
  • Alt-F or Esc-F move forward one word
  • Ctrl-L clear the screen
  • Ctrl-U delete from cursor to the beginning of the line (“undo this whole front part”)
  • Ctrl-K delete from cursor to the end of the line (“kill to end”)
  • Ctrl-W delete the word behind the cursor
  • Ctrl-P previous command (same as up arrow)
  • Ctrl-N next command (same as down arrow)
  • Ctrl-R reverse search through history (Start typing part of a previous command and press Ctrl-R repeatedly to cycle matches.)

Vi-style keybindings (modal editing)

Vi mode is “modal,” meaning you switch between modes: “Insert” mode where you type normally, or “Normal” mode where keys perform editing commands. This feels strange at first, but you can become extremely fast once it clicks. Vi’s reputation is that you can move mountains in a few keystrokes, but with the cost that even simple operations also require a few keystrokes.

To change from the default emacs-style key binding to vi-style for the current shell session, run bindkey -v or set -o vi. To enable vi-style keybinding every time you start an interactive shell, add one of those commands to your shell configuration file (typically ~/.zshrc for zsh or ~/.bashrc for bash). Then either restart your terminal or reload the configuration with source ~/.zshrc.

Once enabled, when you’re typing a command you’re usually in insert mode. Press:

  • Esc switch to normal mode
  • i return to insert mode at the cursor
  • a return to insert mode after the cursor

In normal mode, a typical operation folows the pattern count-operator-motion/object. For example, 3dw deletes forward three words. Omitting the count is typica, for example ci( changes the text inside the current set of parentheses. Common commands include:

Movement of cursor

  • h/l left/right
  • 0 beginning of line
  • $ end of line
  • w forward one word
  • b back one word

Editing (normal mode)

  • x delete character under cursor
  • dw delete a word
  • dd delete the entire line
  • cw change a word (delete word and enter insert mode)
  • D delete from cursor to end of line

History search

  • j/k up/down
  • /text search backward in history for “text”
  • n repeat the last search

A common workflow looks like: type a command; hit Esc; quickly edit with w, b, dw, etc.; press i to insert again; run.

Combining tools

The Unix philosophy is that command line tools are designed to do one thing and to do it well. Therefore, complex operations are created through a composition of tools. This is made possible by managing the communication streams of these tools. Each has 3 standard communication streams: standard input (stdin), standard output (stdout), and standard error (stderr). Often, the keyboard will not be the source of input, but rather it will be from the output generated by other tools and contents of files.

  • < takes input from a file (eg, < file.txt wc)
  • > directs output to a file and overwrites (eg, ls > dir_contents.txt)
  • >> directs output to a file and appends
  • | pipes output from one command to input of another (eg, ls | less)

Common tools for inspecting files, or for gathering and summarizing their contents include:

  • cat for concatenating files; can be used to print file contents in the terminal for short files
  • find for recursively searching a directory hierarchy returning names of files and/or directories
  • wc for counting characters, words, and lines
  • head for obtaining the first few lines
  • tail for obtaining the last few lines
  • sort for sorting the contents of a file
  • uniq for removing adjacent duplicates

More-advanced tools that deserve their own section and explanation

  • grep for matching a Regular Expression pattern in one or more files
  • sed a stream editor for modifying data
  • awk a data processing language named after its creators

Regular Expressions

Sections to add

  • File permissions (read/write/execute)
  • A whole section on each of grep/sed/awk
  • Connecting: ssh/ftp/curl
  • Environment variables (incl. customizing the prompt)
  • Writing programs (incl. if/for/while)