hamacs/zshell.org
Howard Abrams 0743dd1dbc ZShell configuration files
Also, got compilation working things like Docker builds.
2025-05-29 15:25:47 -07:00

12 KiB
Raw Permalink Blame History

Zshell

No, I havent eschewed my beloved Eshell, but at work, Im often required to work with Bash snippets and code. This requires using a standard shell environment. This file includes my notes on Zshell, as well my configuration, when tangled.

This creates the following files:

~/.zshenv
Usually run for every zsh
~/.zshrc
Run for interactive shells … default file when tangling
~/.zlogin
Run for login shells

Path

The all important PATH environment variable, needs my special bin directory.

  export PATH=$HOME/bin:$PATH

Options

When the command is the name of a directory, perform the cd command to that directory:

setopt AUTO_CD

Make cd push the old directory onto the directory stack so that popd always works:

setopt AUTO_PUSHD

Print the working directory after a cd, but only if that was magically expanded:

setopt NO_CD_SILENT

Automatically list choices on an ambiguous completion:

setopt AUTO_LIST

Automatically use menu completion after the second consecutive request for completion:

setopt AUTO_MENU

Try to make the completion list smaller (occupying less lines) by printing the matches in columns with different widths:

setopt LIST_PACKED

On an ambiguous completion, instead of listing possibilities or beeping, insert the first match immediately. Then when completion is requested again, remove the first match and insert the second match, etc.

setopt MENU_COMPLETE

Homebrew

When using Homebrew on a Mac, we need to add its PATH:

  eval $(/opt/homebrew/bin/brew shellenv zsh)

This adds the following environment variables, along with expanding the PATH.

  export HOMEBREW_PREFIX="/opt/homebrew";
  export HOMEBREW_CELLAR="/opt/homebrew/Cellar";
  export HOMEBREW_REPOSITORY="/opt/homebrew";
  [ -z "${MANPATH-}" ] || export MANPATH=":${MANPATH#:}";
  export INFOPATH="/opt/homebrew/share/info:${INFOPATH:-}";

ZShell Styles

The Zsh Book has a nice chapter treatment on zstyle, also, explaining in detail its various fields.

Tab Completion

Use autoload to install compinit, the completion system:

  autoload -U compinit; compinit

Do I need this?

  zstyle ':completion:*:*:cp:*' file-sort modification reverse
  zstyle ':completion:*:*:mv:*' file-sort modification reverse

Selecting options using Tab, arrows, and C-p / C-n:

  zmodload zsh/complist

Do I want to use hyphen-insensitive completion, so that _ and - will be interchangeable?

  HYPHEN_INSENSITIVE="true"

Auto Correction

If you type something wrong, Zshell, by default, prompts to see if you wanted to try something different.

  ENABLE_CORRECTION="true"

What about just fixing it? For this, we update the ZShell line editor:

  autocorrect() {
    zle .spell-word
    zle .$WIDGET
  }

  zle -N accept-line autocorrect
  zle -N magic-space autocorrect

Bind the ability to auto-correct the word to the left with Space or Enter:

  bindkey ' ' magic-space

Waiting Indication

Display red dots whilst waiting for commands to complete.

  COMPLETION_WAITING_DOTS="true"

You can also set it to another string to have that shown instead of the default red dots.

  COMPLETION_WAITING_DOTS="%F{yellow}waiting...%f"

Oh My Zshell

Some plugins for Zshell are nice, so lets install Oh My Zshell:

  sh -c "$(curl -fsSL https://raw.githubusercontent.com/ohmyzsh/ohmyzsh/master/tools/install.sh)"
Cloning Oh My Zsh...
branch 'master' set up to track 'origin/master' by rebasing.
/Users/howard/Library/CloudStorage/Dropbox/org/technical

Looking for an existing zsh config...
Found old .zshrc.pre-oh-my-zsh. Backing up to /Users/howard/.zshrc.pre-oh-my-zsh-2025-03-30_12-22-02
Found /Users/howard/.zshrc. Backing up to /Users/howard/.zshrc.pre-oh-my-zsh
Using the Oh My Zsh template file and adding it to /Users/howard/.zshrc.

         __                                     __
  ____  / /_     ____ ___  __  __   ____  _____/ /_
 / __ \/ __ \   / __ `__ \/ / / /  /_  / / ___/ __ \
/ /_/ / / / /  / / / / / / /_/ /    / /_(__  ) / / /
\____/_/ /_/  /_/ /_/ /_/\__, /    /___/____/_/ /_/
                        /____/                       ....is now installed!


Before you scream Oh My Zsh! look over the `.zshrc` file to select plugins, themes, and options.

• Follow us on X: https://x.com/ohmyzsh
• Join our Discord community: https://discord.gg/ohmyzsh
• Get stickers, t-shirts, coffee mugs and more: https://shop.planetargon.com/collections/oh-my-zsh

Run zsh to try it out.

Set its location:

  export ZSH=$HOME/.oh-my-zsh

Syntax Coloration

The ZShell Syntax Highlighting project provides Fish shell-like syntax highlighting for ZShell. This was my killer feature for using Fish, but I need the standard Bash-compatible syntax. Now I can have both. Lets install this project in coordination with Oh My Zshell:

  git clone https://github.com/zsh-users/zsh-syntax-highlighting.git ${ZSH_CUSTOM:-~/.oh-my-zsh/custom}/plugins/zsh-syntax-highlighting

Plugins

Configure the plugins, making sure to not use git, as the aliases are a pain to remember when I already have a superior Git interface in Emacs.

colorize
syntax-highlight file contents, so install Pygments first. Then call ccat and cless (see /git/howard/hamacs/src/branch/main/Aliases).
direnv
to support the direnv virtual environment project.
gnu-utils
bind the GNU flavor for standard utils, like gfind to the normal version, e.g. find.
iterm2
while fully configured below, configures the interaction with the MacOS application, iTerm2.
macos

adds new functions that work better with MacOS terminals and the Finder. I like:

tab
To open a new terminal tab
cdf
To open a directory in the Finder, meh. Why not change this to open it in dired in Emacs?
quick-look
To view a file
zbell
To beep when a long running command has completed. Similar to my beep command.
  plugins=(colorize direnv gnu-utils iterm2 macos zbell zsh-syntax-highlighting)

Notice the iterm2 plugin as well as the macos plugins that would be nice to figure out how to make them optionally added (although I believe they check themselves).

The trick is to install some base-level plugins, and then, on my work computer, install more.

  if hostname | grep AL33 >/dev/null
  then
    plugins+=(ansible argocd docker helm kubectl)
  fi

Now that Ive filled in the plugins variable, load OMZ and the plugins:

  source $ZSH/oh-my-zsh.sh

Do we want to waste time during startup to update this? These can be:

disabled
disable automatic updates
auto
update automatically without asking
reminder
remind me to update when it's time
  zstyle ':omz:update' mode auto
  zstyle ':omz:update' frequency 13

Well Check every 13 days.

Prompt

Either keep it simple:

  PS1='%(?.%F{green}.%F{red})$%f%b '

Oh use the absolute over-the-top bling associated with Oh My Zshells themes, like:

  ZSH_THEME="robbyrussell"

iTerm2

On Mac systems, I like the iTerm2 application, and we can enable shell integration, either via the old school way, or just rely on the plugin above:

  test -e "${HOME}/.iterm2_shell_integration.zsh" && source "${HOME}/.iterm2_shell_integration.zsh"

Also, while use the title command to change the Terminals title bar, dont let the prompt or other Zshell features do that:

  DISABLE_AUTO_TITLE="true"

Favorite feature is the Status Bar at the bottom of the screen that shows the Git branch, current working directory, etc. This allows my prompt to be much shorter. What other information I want has changed over the years, but I define this information with this function:

Currently, I show the currently defined Kube namespace.

  function iterm2_print_user_vars() {
    iterm2_set_user_var kubecontext $($ yq '.users[0].name' ~/.kube/config):$(kubectl config view --minify --output 'jsonpath={..namespace}')

    # Correct version:
    # iterm2_set_user_var kubecontext $(kubectl config current-context):$(kubectl config view --minify --output 'jsonpath={..namespace}')
    # Faster version:
    # iterm2_set_user_var kubecontext $(awk '/^current-context:/{print $2;exit;}' <~/.kube/config)
  }

Emacs

While Oh My Zshell has an emacs plugin, Im not crazy about it. I guess I need more control.

While it should figure out (as Emacs keybindings are the default), this is how we ensure it:

  bindkey -e

Where be the emacsclient, and how should we call it?

  export EMACS="emacsclient --socket-name personal"

Which needs to be overwritten on my Work computer:

  if hostname | grep AL33 >/dev/null
  then
    export EMACS="emacsclient --socket-name work"
  fi

The EDITOR variable that some programs use to edit files from the command line:

  export EDITOR="$EMACS --tty"
  export VISUAL="$EMACS --create-frame"

With these variables defined, we can create simple aliases:

  alias e="$EDITOR"
  alias te="$EDITOR"
  alias ee="$EMACS --create-frame"
  alias eee="$EMACS --create-frame --no-wait"

Aliases

Assuming weve installed lsd and other colorized features:

  alias ls=lsd
  alias less=cless
  alias cat=ccat

And some abstractions over SSH:

  alias ossh="ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -o loglevel=ERROR"

Final Message

For sensitive work-related environment variables, store them elsewhere, and load them:

  test -e "${HOME}/.zshenv-work" && source "${HOME}/.zshenv-work"

To let us know we read the ~/.zshrc file:

  echo "🐚 ZShell Session"