Switching to zsh from bash

Published » 2019-11-22

In preparation from my migration to Catalina this weekend, I put together a quick list of changes to keep in mind when using Bash over Zsh. Keep in mind that a modern build of Bash has most of these features but I'll always jump at the opportunity to learn something new.

Here's my humble original bashrc file (GitHub).

source $HOME/.aliases

# Read our local bashrc.
source $HOME/.bashrc_local

# Set environmental variables.
EDITOR=vim

# Turn off system bell if we're not in an SSH session.
if [ -z ${SSH_TTY+x} ]; then
  if [ -z ${DISPLAY+x} ]; then 
    command -v setterm
    if [ $? -eq 0 ]; then
      setterm --blength 0
    fi
  else xset -b
  fi
fi

# Create the prompt.
BLUE="\[\e[0;34m\]"
OFF="\[\033[m\]"
PS1="$%{BLUE%} $ $%{OFF%}"

This file also turns off the system bell - very useful for new machines or virtual machines. .bashrc_local isn't checked in version control. It contains the emoji for the specific machine. In the case of Apple it's just: 'EMOJI='.

Based on stepping through a lot of ZSH configs (see footer) here's my changes.

Migrating existing bash config to zsh

To prepare myself for the migration I spent an hour or so reading man zshmisc. First I copied my bashrc into a new file named ~/.zshrc. Then, because the colour escape sequences weren't working, I updated the BLUE and OFF lines to be compatible.

To test colours, I made heavy use of print -P '<escape sequence>' and wrapping the colours in %{...%}.

  • BLUE="%F{blue}"
  • OFF="%f"
  • PROMPT="\$"
  • PS1="${EMOJI} %{${BLUE}%}${PROMPT}${OFF} "

As my original config was very simple, this was enough.

Taking advantage of ZSH functionality

Now zsh has alot of built in functionality that I was reluctant to take advantage of for compatibility reasons -- however, I spend a lot of time in the terminal so I found this few changes a good compromise between a supercharged terminal and a minimalist configuration.

Start by reading man zshbuiltins.

Options

Configure options by running setopt OPTIONNAME and setopt noOPTIONNAME to unset. http://zsh.sourceforge.net/Doc/Release/Options.html#Options

AUTO_CD

Automatically move into a new directory if only that name is typed at the prompt.

$ pwd
$ /home/local
$ bin
$ pwd
$ /home/local/bin

APPEND_HISTORY

All zsh sessions append their command history to a single file instead of replacing it. Note that the history file isn't updated until that zsh instance exits.

EXTENDED_HISTORY

Also save the command's timestamp and duration in the history file. Extremely useful for timing commands.

HISTNOSTORE

Don't store invocations of the builtin history in the history file.

Functions

Completion system

I opted to start using the autocompletion system for smarter tab completion.

$ ls -<TAB>              
-1  -- single column output
-A  -- list all except . and ..
-B  -- print octal escapes for control characters
-C  -- list entries in columns sorted vertically
[.. snip ..]

Modules

zsh includes a lot of built-in modules to further change the behaviour of the shell. I didn't opt to enable them in my config.

Final configuration

Here's my final zsh config with all the above taken into account. I imagine 3 months down the road this file will have even more changes. You can always check the latest in xocite/dotrc/.zshrc @ GitHub.

# Read our aliases file.
source $HOME/.aliases

# Read our local zshrc.
source $HOME/.zshrc_local

# Set environmental variables.
EDITOR=vim

# Turn off system bell if we're not in an SSH session.
if [ -z ${SSH_TTY+x} ]; then
  if [ -z ${DISPLAY+x} ]; then 
    command -v setterm
    if [ $? -eq 0 ]; then
      setterm --blength 0
    fi
  else xset -b
  fi
fi


# Set options
setopt AUTO_CD
setopt APPEND_HISTORY
setopt EXTENDED_HISTORY
setopt HIST_NO_STORE

# Load functions
autoload -U compinit && compinit

# Create the prompt.
BLUE="%F{blue}"
OFF="%f"
PROMPT="\$"
PS1="${EMOJI} %{${BLUE}%}${PROMPT}${OFF} "

Further reading

  • zsh FAQ link
  • Peter Karbowski's zsh config link
  • zsh manual link