Complex Color Patterns for Shell Prompts
Letter-level alternating colors for bash and zsh shells.
Posted on April 1, 2021
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 is obvious to see at this point was an April Fool's joke).
Colored Shell Prompts?! With Emojis?!
Most of us devs know we can color our shell prompts with various colors, and 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 had memorized and 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":
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:
or this:
or, my current setup, like this:
The Repository
The 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!
As far as I know, I'm the first person to ever have done this! (Or at least published it) 😎 I know why now - because bash and zsh syntax makes creating such color patterns a massive headache! 😅
I believe I've saved you that headache by creating a repository for the snippets and have also pasted the code snippets below.
The Code
The 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! For the impatient, a working script for both bash and zsh shells is below, but I can't promise that these following snippets are the most up-to-date versions.
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 repository to submit the code for a new shell!
Thanks!
That's all for now. I'm off to enjoy my birthday, but I may at some point write an additional post with a walkthrough of both functions - as it turns out, there are some annoying and interesting differences between bash and zsh - the most unforgivable is that loops in zsh start at 1 🙄.
Cheers! 🍺
Chris