home ~ socials ~ other projects

Command Line Prompt Customization without Oh My Zsh

A Quick Look

Prompts for command lines are customizable. Mine looks like this:

A command line prompt showing four lines. The first starts with a greater than sign, then my computer's name the current working directory, an OK, the text VENV, then main in square brackets followed by alanwsmith.com and finally a time of 9:22pm. The second line shows the greater than sign followed by the word false. The third line is the same as the first except OK has been replaced with ERROR: 1. The fourth line is just the greater than character follwed by the cursor

The breakdown is:

  • A starting > character
  • My computer's name
  • The current directory
  • Status of the last command (OK or ERROR #)
  • Indicator that the directory is inside a python virtual environment
  • Current git repo branch
  • Marketing URL for streams and VODs
  • The time

All that stuff is on one line. The following line has another > and is where I actually type stuff1.

Tools for the Job

Different tools provide easy ways to do the customization. Oh My Zsh2 is a big one. It provides a bunch of stuff, including themes that control the look of the prompt. The powerlevel10k3 theme is one I've seen around and played with.

Those tools felt to magic for me. They were doing a bunch of stuff that I didn't know about or need.

I like to know what's going on with my machine. Taking the time to research everything Oh My Zsh was doing wasn't a priority. I yoinked it.

Doing It My Way

Seeing Oh My Zsh did spark my interest in customizing my prompt. But, I wanted to understand what was happening and how to do it.

The answer is to update your ~/.zshrc file3. Here's the parts from mine that customize the prompt:

#########################################################
# Define Prompt Colors and Newline String
# Note that different terminal apps might
# treat colors differently and might
# only use ASCII numbered colors instead
# of the ones that start with a `#`

BG_ON='%K{233}'
BG_OFF='%k'
FG_ON='%F{#6b560e}'
FG_OFF='%f'
FG_CARET='%F{242}'
FG_DOT='%F{239}'
FG_OK='%F{22}'
FG_ER='%F{124}'
NEWLINE=$'\n'


#########################################################
# Make a display to show if the last command
# was successful or not

function prompt_previous_exit_code() {
    if [[ $? -eq 0 ]]; then
        echo " ${FG_DOT}· ${FG_OK}OK${FG_ON}"
    else
        echo " ${FG_DOT}· ${FG_ER}ERROR: $?${FG_ON}"
    fi 
}

#########################################################
# Show current git branch if there is one

function prompt_git_branch() {
    BRANCH_NAME=$(git branch --show-current 2> /dev/null)
    if [ $? -eq 0 ]; then
        echo -n " ${FG_DOT}· [$BRANCH_NAME]${FG_ON}"
    else
        echo -n "${FG_DOT}${FG_ON}"
    fi 
}

#########################################################
# Show if you're in a python virtual environment

function prompt_python_venv() {
    if [ -n "$VIRTUAL_ENV" ]; then 
        echo -n " ${FG_DOT}· VENV${FG_ON}"
    else
        echo -n "${FG_DOT}${FG_ON}"
    fi
}

#########################################################
# Date String

function prompt_date_string() {
   echo " ${FG_DOT}· $(date "+%-I:%M%p" | tr '[:upper:]' '[:lower:]')"
}


#########################################################
# Bio URL 

function bio_url() {
    echo " ${FG_DOT}· alanwsmith.com"
}

#########################################################
# Set The Prompt

function precmd() {
   PROMPT_EXIT_STRING=$(prompt_previous_exit_code)
   L1="${FG_CARET}> ${FG_ON}dojo $(dirs)"
   L1="${L1}${PROMPT_EXIT_STRING}"
   L1="${L1}$(prompt_python_venv)"
   L1="${L1}$(prompt_git_branch)"
   L1="${L1}$(bio_url)"
   L1="${L1}$(prompt_date_string)"
   L1="${L1} ${FG_DOT}${PROMPT_DATE_STRING}${FG_ON} "
   PS1="${L1}${NEWLINE}${FG_CARET}>${FG_OFF} "
}

A Quick Tour

At the highest level, it call comes down to setting the PS1 variable in the precmd() function. Start there and backtrack to see how everything ties together.

Functions do all the work. You can make them do pretty much anything you want. The biggest thing to keep in mind is that each one runs every time your run a command before you see the prompt again.

For example, You could have a function that calls out to a service to get the weather. You'll get a really good idea of how long the service takes because you'll feel the delay between every promptlarge git trees.

Go Forth and Customize

Tweaking the command line prompt is one of those fiddly things that's a lot of fun. It's also super handy in terms of providing info about your current environment. Whether you make your own functions or decided to go with something like Oh My Zsh it's well worth the time.

-a

-- end of line --

Footnotes

Most of the customized prompt examples I see place the cursor on the same line directly after all the other text. That drives me nuts since its starting column moves around dependent on what the rest of the text is doing (e.g. directories with different length path names)

I vastly prefer having the cursor on its own line. It's always in the same spot regardless of what is above it.

"Oh My Zsh is a delightful, open source, community-driven framework for managing your Zsh configuration."

A customization theme for zsh. Been a while since I looked at it and sounds like it's deprecated now. It places too much emphasis on the prompt elements for my linking, but it'll show you some more of what's possible.

I'm talking about zsh in this post. All the same stuff applies to the main shells (e.g. bash) each handles things a little different but the ideas are the same.