Awesome Colors for Shell Prompts!

Letter-level alternating colors for the bash and zsh shells.

Posted on April 1, 2021

Tags:

It's My Birthday!

No, that's not an April Fool's joke, and yes, I've heard them all before, har, har, har 😑.

Anyway, I typically like to make a nice little blog post on my birthday. My previous birthday posts have included a post about giving up as a creator and that I was shutting down the blog (which obvious to see at this point was an April Fool's joke).

Colored Shell Prompts

Most of us devs know we can color our shell prompts with various colors, and typically in shell syntax it is some sort of ugly looking formatting string like \e[31m - that one is for the color red, which I'm sure you knew right away, right? 😂

Well, I took a deep dive recently on the whole shell prompt formatting story recently, after creating my Full Stack Course "Bash Commands and Scripting - from Beginner to Expert":

Loading...

All that aside, what if I told you that you could style your prompt in an advanced fashion, to make it look something like this:

A rasta bash prompt

or this:

A star-spangled bash prompt

or, my current setup, like this:

A rainbow bash prompt.

The Code

The code repository is here. There you'll find all the details about the actual color implementation and the steps you need to take to get these cool colors to show up in your shell!

The Implementations

bash

function buildColorPrompt() {

    # I always like showing what directory I am in (special character "\w" in PS1) - store the equivalent in this 'directory' variable
    directory=$(pwd)

    # Modify these to whatever you'd like!
    PROMPT_TEXT="awesome-shell-prompt-colors@awesome-machine [$directory] "

    # Colors seperated by comma - acceptable values are:
    # black, white, red, green, yellow, blue, magenta, cyan, light gray, light red, light green, light yellow, light blue, light magenta, light cyan
    PROMPT_COLORS="red,white,blue"

    # Colors!
    BLACK="\e[30m"
    WHITE="\e[97m"
    RED="\e[31m"
    GREEN="\e[32m"
    YELLOW="\e[33m"
    BLUE="\e[34m"
    MAGENTA="\e[35m"
    CYAN="\e[36m"
    LIGHT_GRAY="\e[37m"
    DARK_GRAY="\e[90m"
    LIGHT_RED="\e[91m"
    LIGHT_GREEN="\e[92m"
    LIGHT_YELLOW="\e[93m"
    LIGHT_BLUE="\e[94m"
    LIGHT_MAGENTA="\e[95m"
    LIGHT_CYAN="\e[96m"

    # End formatting string
    END_FORMATTING="\[\e[0m\]"

    # split PROMPT_COLORS into array
    count=0
    IFS=','
    for x in $PROMPT_COLORS
    do
        colors_array[$count]=$x
        ((count=count+1))
    done
    unset IFS

    # break PROMPT_TEXT into character array
    letters=()
    for (( i=0 ; i < ${#PROMPT_TEXT} ; i++ )) {
        letters[$i]=${PROMPT_TEXT:$i:1}
    }

    # build prompt with colors
    color_index=0
    ps1='\['
    for (( i=0 ; i < ${#letters[@]} ; i++ )) {
        # Determine color in this giant case statement
        color="${colors_array[color_index]}"
        case $color in
            "black")
                COLOR=$BLACK
                ;;
            "red")
                COLOR=$RED
                ;;
            "green")
                COLOR=$GREEN
                ;;
            "yellow")
                COLOR=$YELLOW
                ;;
            "blue")
                COLOR=$BLUE
                ;;
            "magenta")
                COLOR=$MAGENTA
                ;;
            "cyan")
                COLOR=$CYAN
                ;;
            "light gray")
                COLOR=$LIGHT_GRAY
                ;;
            "dark gray")
                COLOR=$DARK_GRAY
                ;;
            "light red")
                COLOR=$LIGHT_RED
                ;;
            "light green")
                COLOR=$LIGHT_GREEN
                ;;
            "light yellow")
                COLOR=$LIGHT_YELLOW
                ;;
            "light blue")
                COLOR=$LIGHT_BLUE
                ;;
            "light magenta")
                COLOR=$LIGHT_MAGENTA
                ;;
            "light cyan")
                COLOR=$LIGHT_CYAN
                ;;
            "white")
                COLOR=$WHITE
                ;;
            *)
                COLOR=$WHITE
                ;;
        esac

        # add to ps1 var - color, then letter, then the end formatter
        ps1+=$COLOR"${letters[$i]}"

        # reset color index if we are at the end of the color array, otherwise increment it
        if (( $color_index == ${#colors_array[@]} - 1 ))
        then
            color_index=0
        else
            ((color_index=color_index+1))
        fi
    }
    ps1+="$END_FORMATTING\]"

    # Finally: set the PS1 variable
    PS1=$ps1
}

# Set the special bash variable PROMPT_COMMAND to our custom function
PROMPT_COMMAND=buildColorPrompt;

zsh

function buildColorPrompt() {

    # I always like showing what directory I am in
    directory=$(pwd)

    # Modify these to whatever you'd like!
    PROMPT_TEXT="youruser@yourmachine [$directory]"

    # Comma seperated colors - as many or as few as you'd like
    PROMPT_COLORS="15"

    # This will be the color of everything in the input part of the prompt (here set to 15 = white)
    PROMPT_INPUT_COLOR="15"

    # split PROMPT_COLORS into array
    colors_array=("${(@s/,/)PROMPT_COLORS}") # @ modifier

    # break PROMPT_TEXT into character array
    letters=()
    for (( i=1 ; i < ${#PROMPT_TEXT}+1 ; i++ )) {
        letters[$i]=${PROMPT_TEXT:$i-1:1}
    }

    # build prompt with colors
    color_index=1
    ps1=""
    for (( i=1 ; i < ${#letters[@]}+1 ; i++ )) {
        # Determine color in this giant case statement
        color="${colors_array[color_index]}"

        # add to ps1 var - color, then letter, then the end formatter
        ps1+="%F{$color}${letters[$i]}"

        # reset color index if we are at the end of the color array, otherwise increment it
        if (( $color_index == ${#colors_array[@]} ))
        then
            color_index=1
        else
            ((color_index=color_index+1))
        fi
    }

    # end color formating
    ps1+="%F{$PROMPT_INPUT_COLOR} %# "

    # Finally: set the PROMPT variable
    PROMPT=$ps1
}

# set the precmd() hook to our custom function
precmd() {
   buildColorPrompt;
}

You'll also find these snippets under the devops section of the snippets page.

Go ahead and paste these right into your .bash_profile or .zprofile, respectively, depending on what terminal you are using. Or, make a pull request on the repsoitory to submit the code for a new shell!

Thanks!

That's all for now. I'm off to enjoy my birthday, but I may improve this post with a walkthrough of both functions very soon - as it turns out, there are some annoying and interesting differences between bash and zsh - the most unforgivable that loops in zsh start at 1 🙄.

Cheers 🍺

Chris

Next or Previous Post:

Or find more posts by tag: