Or: How I brought shell scripting from 1975 into 1995.
The Problem: ANSI Escape Codes Are Tedious AF
Let's be honest - working with terminal colours in bash is painful. You either memorise cryptic escape sequences like \033[38;5;196m
or constantly look them up. Want to make something bright AND red? Hope you enjoy typing \033[1;38;5;196m
every time.
As someone who comes from MUD development, I am accustomed to simple colour tokens like %^RED%^
or %^RESET%^
. Why couldn't shell scripts be that elegant?
How many have
- cheat sheets for commonly used ANSI sequences? or
- bookmarks to ANSI escape code??
- a litany of shell scripts to reference as previous examples on how to do a colour???
Just me? All right.
The Old Solution
So we came up with shortcuts to use colours because early on, we decided we collectively disliked the idea of typing bare ANSI sequences everywhere. And, as a solution, we decided on this norm:
# Colours for output
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m' # No Colour, reset everything to default.
echo -e "${RED}✗ Error: Something went wrong${NC}"
echo -e "${GREEN}✓ Success: Operation completed${NC}"
echo -e "${YELLOW}⚠ Warning: Check your input${NC}"
Yes, this is perfectly fine. And I don't hate this pattern, and I think it's definitely a mostly portable way to achieve it. There are other ways, that are even more portable, but the above is fairly typical nowadays. At least, that's what I see most commonly. YMMV, YEMV, YEET.
Anyway, while I don't hate it, I don't love it.
The Solution: Meet colours
So I built a tiny bash library that makes terminal colours actually pleasant to use:
#!/bin/bash -i
. ~/bin/colours
echo "$(F 196)This is red text$(RST)"
echo "$(F 046)$(B 196)Green text on red background$(RST)"
echo "$(S BI)$(F 220)Bright italic yellow text$(RST)"
That's it. Clean, readable, and you can actually remember the function names. And with frequent usage, as we do in the MUD community, you begin to remember the colour codes you prefer and/or frequently use! Way easier to build muscle memory in the brain. Or, what limited amount remains for us old-timer-MUDders.
The API: Short and Sweet
The whole library is just 5 functions:
-
F(n)
- Set foreground colour (0-255) -
B(n)
- Set background colour (0-255) -
S(codes)
- Composably set styles (B
old,I
talic,U
nderline, etc.) -
U(codes)
- Composably unset specific styles -
RST()
- Reset everything
Want bright italic underlined text? Just $(S BIU)
. Want to turn off just the italic? $(U I)
.
Is it portable?
lol, no. But I don't need portability for my scripts. Portable is definitely important for numerous use cases, but not in any of mine. Maybe someone will take it and make a portable version, or maybe there is already a portable version that is better that someone else wrote, I don't know.
The Secret Sauce Source: Dual Purpose Design
Here's the fun bit - colours
works as both a library AND a colour picker.
As a library: Source it in your scripts for the API
$COLOURISER # I set this in my .bashrc
echo "$(F 219)Pretty colours!$(RST)"
As a colour picker: Run it directly to see all 256 colours with their numbers
$ colours
No more guessing which shade of blue is colour 75 vs 81. Just run it, pick the number you want, done.
Real-World Usage
I use this in all my utility scripts now. Compare these two approaches:
Before (the bad old days):
echo -e "\033[32m✓ Success\033[0m"
echo -e "\033[31m✗ Failed\033[0m"
echo -e "\033[33m⚠ Warning\033[0m"
After (living in the future):
echo "$(F 46)✓ Success$(RST)"
echo "$(F 196)✗ Failed$(RST)"
echo "$(F 220)⚠ Warning$(RST)"
So much cleaner! And when I want to change the exact shade of green, I just run colours
, pick a new number, and update one digit.
The MUD Heritage
This design comes from years of MUD development where colour codes need to be:
- Fast to type (you use them constantly)
- Easy to read (mixed with lots of text)
- Memorable (no looking up codes)
Turns out these same principles make shell scripting way more pleasant too.
Get It
Because life's too short for echo -e "\033[1;38;5;196;48;5;21m"
.
What's your favourite terminal colour? Mine's 38 - a lovely blue that makes shell messages feel less hostile.
Script
License
This script is released under the Unlicense because idgaf. Below is it, in all of its splendour and gloury. 🇨🇦
Behold
#!/bin/bash -i
set -euo pipefail
# colours - 256 Colour Terminal Display and Library Script
#
# Clean grid with actual colour numbers for use in other scripts
#
# Copyright: nope
# Warranty: heh
# License: The Unlicense (https://unlicense.org/)
# - With love from gesslar and Claude 🥰 - 2025
#
# Usage: colours - Display colour chart
# source colours - Load colour functions into shell
# $COLOURISER - Use via environment variable
#
# Functions provided when sourced:
# RST() - Reset all formatting
# F(n) - Set foreground colour (0-255)
# B(n) - Set background colour (0-255)
# S(codes) - Set styles (B=bright, I=italic, U=underline, L=blink, R=reverse, S=strikethrough)
# U(codes) - Unset styles
#
# Setup Instructions:
#
# 1. Create an environment variable in your main shell script (ex: .bashrc)
# pointing to this file. Example:
#
# bash-like: export COLOURISER=". ~/bin/colours"
# fish-like: set -gx COLOURISER ". $(realpath ~/bin/colours)"
#
# 2. Execute this script at the top of your script to bring in the colour
# functions. Example:
#
# #!/bin/bash
#
# $COLOURISER
#
# echo "$(F 219)This is foreground colour 219$(RST)"
# echo "$(B 196)This is bright red background$(RST)"
# echo "$(F 046)$(B 196)Green text on red background$(RST)"
#
# 3. 💰💰💰💰💰💰💰
# #############################################################################
# Main colour functions
# #############################################################################
# Reset function ##############################################################
RST() { echo -ne '\033[0m'; }
# Foreground colour function ##################################################
F() { echo -ne "\033[38;5;${1}m"; }
# Background colour function ##################################################
B() { echo -ne "\033[48;5;${1}m"; }
# Style function that accepts multiple codes ##################################
S() {
local codes="$1"
local output=""
for (( i=0; i<${#codes}; i++ )); do
case "${codes:$i:1}" in
B) output+='\033[1m' ;; # Bright
D) output+='\033[2m' ;; # Dim
I) output+='\033[3m' ;; # Italic
U) output+='\033[4m' ;; # Underline
L) output+='\033[5m' ;; # Blink
R) output+='\033[7m' ;; # Reverse
S) output+='\033[9m' ;; # Strikethrough
esac
done
echo -ne "$output"
}
# Unset/turn off specific styles ##############################################
U() {
local codes="$1"
local output=""
for (( i=0; i<${#codes}; i++ )); do
case "${codes:$i:1}" in
B) output+='\033[22m' ;; # Turn off bright/dim
D) output+='\033[22m' ;; # Turn off bright/dim
I) output+='\033[23m' ;; # Turn off italic
U) output+='\033[24m' ;; # Turn off underline
L) output+='\033[25m' ;; # Turn off blink
R) output+='\033[27m' ;; # Turn off reverse
S) output+='\033[29m' ;; # Turn off strikethrough
esac
done
echo -ne "$output"
}
# #############################################################################
# Display the colour chart. Pick a colour, _any colour_!!!!
# #############################################################################
# Only show the chart if the script is run directly, not sourced
if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then
echo -e " $(F 227)-= $(F 198)High colour List $(F 227)=-$(RST)"
echo
# Rainbow colours (16-231) - 3 sections, 6 rows each, 12 colours per row
for i in {0..2}; do
for j in {0..5}; do
for k in {0..11}; do
colour=$((16 + i * (6 * 12) + j + k * 6))
printf "$(F $colour)%03d$(RST) " "$colour"
done
echo
done
done
echo
# Grayscale (232-255) - 2 rows, 12 colours each
for i in {0..1}; do
for j in {0..11}; do
colour=$((232 + i * 12 + j))
printf "$(F $colour)%03d$(RST) " "$colour"
done
echo
done
echo
# Base colours (0-15) - 2 rows, 8 colours each
for i in {0..1}; do
for j in {0..7}; do
colour=$((i * 8 + j))
printf "$(F $colour)%03d$(RST) " "$colour"
done
echo
done
fi
Whee!
This was my first post ever, how did I do?
Top comments (0)