Skip to content
Snippets Groups Projects
Commit e479a50e authored by Hahn Axel (hahn)'s avatar Hahn Axel (hahn)
Browse files

add color.class

parent 0d715cf5
Branches
No related tags found
1 merge request!129Db Profiles
#!/bin/bash
# ======================================================================
#
# COLORS
#
# a few shell functions for colored output
#
# ----------------------------------------------------------------------
# License: GPL 3.0
# Source: <https://github.com/axelhahn/bash_colorfunctions>
# Docs: <https://www.axel-hahn.de/docs/bash_colorfunctions/>
# ----------------------------------------------------------------------
# 2023-08-09 ahahn 0.1 initial lines
# 2023-08-09 ahahn 0.2 hide output of regex test with grep
# 2023-08-13 ahahn 0.3 introduce of color presets with foreground and background
# 2023-08-13 ahahn 0.4 list presets, debug, count of colors
# 2023-08-13 ahahn 0.5 support of RGB hex code
# 2023-08-14 ahahn 0.6 fix setting fg and bg as RGB hex code
# 2023-08-14 ahahn 0.7 remove color.ansi; respect NO_COLOR=1
# 2023-08-16 ahahn 0.8 add function color.preset
# ======================================================================
_VERSION=0.8
typeset -i COLOR_DEBUG; COLOR_DEBUG=0
# ----------------------------------------------------------------------
# CONSTANTS
# ----------------------------------------------------------------------
declare -A BGCOLOR_CODE
declare -A COLOR_CODE
# background colors
BGCOLOR_CODE[black]="40"
BGCOLOR_CODE[red]="41"
BGCOLOR_CODE[green]="42"
BGCOLOR_CODE[brown]="43"
BGCOLOR_CODE[blue]="44"
BGCOLOR_CODE[purple]="45"
BGCOLOR_CODE[cyan]="46"
BGCOLOR_CODE[lightgray]="47"
BGCOLOR_CODE[darkgray]="1;40"
BGCOLOR_CODE[lightred]="1;41"
BGCOLOR_CODE[lightgreen]="1;42"
BGCOLOR_CODE[yellow]="1;43"
BGCOLOR_CODE[lightblue]="1;44"
BGCOLOR_CODE[lightpurple]="1;45"
BGCOLOR_CODE[lightcyan]="1;46"
BGCOLOR_CODE[white]="1;47"
# foreground colors
COLOR_CODE[black]="30"
COLOR_CODE[red]="31"
COLOR_CODE[green]="32"
COLOR_CODE[brown]="33"
COLOR_CODE[blue]="34"
COLOR_CODE[purple]="35"
COLOR_CODE[cyan]="36"
COLOR_CODE[lightgray]="37"
COLOR_CODE[darkgray]="1;30"
COLOR_CODE[lightred]="1;31"
COLOR_CODE[lightgreen]="1;32"
COLOR_CODE[yellow]="1;33"
COLOR_CODE[lightblue]="1;34"
COLOR_CODE[lightpurple]="1;35"
COLOR_CODE[lightcyan]="1;36"
COLOR_CODE[white]="1;37"
# custom presets as array of foreground and background color
#
# +--- the label is part of the variable
# |
# v
# COLOR_PRESET_error=("white" "red")
# COLOR_PRESET_ok=("white" "green")
# ----------------------------------------------------------------------
# PRIVATE FUNCTIONS
# ----------------------------------------------------------------------
# write debug output - if debugging is enabled
# Its output is written to STDERR
# param string text to show
function color.__wd(){
test "$COLOR_DEBUG" = "1" && >&2 echo "DEBUG: $*"
}
# test, if given value is a known color name
# param string colorname to test
function color.__iscolorname(){
test -n "${COLOR_CODE[$1]}" && return 0
return 1
}
# test, if given value is a value 0..7
# param string color to test
function color.__iscolorcode(){
test "$1" = "0" && return 0
test "$1" = "1" && return 0
test "$1" = "2" && return 0
test "$1" = "3" && return 0
test "$1" = "4" && return 0
test "$1" = "5" && return 0
test "$1" = "6" && return 0
test "$1" = "7" && return 0
return 1
}
# test, if given value is an ansi code
# param string color to test
function color.__iscolorvalue(){
if grep -E "^([01];|)[34][0-7]$" >/dev/null <<< "$1" ; then
return 0
fi
return 1
}
# test, if given value is an rgb hexcode eg. #80a0f0
# param string color to test
function color.__isrgbhex(){
if grep -iE "^#[0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f]$" >/dev/null <<< "$1" ; then
return 0
fi
return 1
}
# convert rgb hex code eg. #80a0f0 to 3 decimal values
# output us a string with space separated values for red, green, blue
# param string color as "#RRGGBB" to convert
function color.__getrgb(){
local _r
local _g
local _b
if color.__isrgbhex "$1"; then
_r=$( cut -c 2,3 <<< "$1" )
_g=$( cut -c 4,5 <<< "$1" )
_b=$( cut -c 6,7 <<< "$1" )
echo "$((16#$_r)) $((16#$_g)) $((16#$_b))"
fi
}
# test, if given value is a color that can be one of
# - colorname
# - value 0..7
# - ansi code
# param string color to test
function color.__isacolor(){
if color.__iscolorname "$1"; then return 0; fi
if color.__iscolorcode "$1"; then return 0; fi
if color.__iscolorvalue "$1"; then return 0; fi
if color.__isrgbhex "$1"; then return 0; fi
color.__wd "$FUNCNAME is acolor: $1 --> No"
return 1
}
# test, if given value is an existing preset
# param string color to test
function color.__isapreset(){
local _colorset
eval "_colorset=\$COLOR_PRESET_${1}"
test -n "$_colorset" && return 0
return 1
}
# respect NO_COLOR=1
# return 1 if colors are allowed to be used.
function color.__usecolor(){
test "$NO_COLOR" = "1" && return 1
return 0
}
# set foreground or background
# param string color as
# - basic color 0..7 OR
# - color name eg. "black" OR
# - a valid color value eg. "1;30" OR
# - a hex code eg. "#10404f"
# param integer what to set; '3' for for foreground or '4' for background colors
function color.__fgorbg(){
local _color="$1"
local _prefix="$2"
color.__wd "$FUNCNAME $1 $2"
if color.__iscolorname "${_color}"; then
color.__wd "yep, ${_color} is a color name."
test "$_prefix" = "3" && color.set "${COLOR_CODE[${_color}]}"
test "$_prefix" = "4" && color.set "${BGCOLOR_CODE[${_color}]}"
else
if color.__iscolorcode "${_color}"; then
color.__wd "yep, ${_color} is a color code."
else
if color.__iscolorvalue "${_color}"; then
color.__wd "yep, ${_color} is a color value."
color.set "${_color}"
else
if color.__isrgbhex "${_color}"; then
local _r
local _g
local _b
read -r _r _g _b <<< $( color.__getrgb "${_color}" )
color.set "${_prefix}8;2;$_r;$_g;$_b"
else
>&2 echo "ERROR: color '${_color}' is not a name nor a value between 0..7 nor a valid color value nor RGB."
fi
fi
fi
fi
}
# ----------------------------------------------------------------------
# FUNCTIONS :: helpers
# ----------------------------------------------------------------------
# get count of colors in the current terminal
function color.count(){
tput colors
}
# enable debug flag
function color.debugon(){
COLOR_DEBUG=1
color.__wd "$FUNCNAME - debugging is enabled now"
}
# disable debug flag
function color.debugoff(){
color.__wd "$FUNCNAME - disabling debugging now"
COLOR_DEBUG=0
}
# show debugging status
function color.debugstatus(){
echo -n "INFO: color.debug - debugging is "
if [ $COLOR_DEBUG -eq 0 ]; then
echo "DISABLED"
else
echo "ENABLED"
fi
}
# show help
function color.help(){
local _self; _self='[path]/color.class.sh'
color.reset
local _debug=$COLOR_DEBUG
COLOR_DEBUG=0
echo "_______________________________________________________________________________"
echo
color.echo "red" " ### ### # ### ####"
color.echo "yellow" " # # # # # # # #"
color.echo "white" " # # # # # # ####"
color.echo "yellow" " # # # # # # # #"
color.echo "red" " ### ### ##### ### # #"
echo "_________________________________________________________________________/ v$_VERSION"
echo
sed "s#^ ##g" << EOH
HELP:
'color' is a class like component to simplify the handling of ansi colors and keeps
the color settings readable. A set NO_COLOR=1 will be respected.
Author: Axel Hahn
License: GNU GPL 3.0
Source: <https://github.com/axelhahn/bash_colorfunctions>
Docs: <https://www.axel-hahn.de/docs/bash_colorfunctions/>
FUNCTIONS:
---------- Information:
color.help this help
color.list show a table with valid color names
color.presets show a table with defined custom presets
color.count get count of colors in the current terminal
color.debugon enable debugging
color.debugoff disable debugging
color.debugstatus show debugstatus
---------- Colored output:
color.bg COLOR (COLOR2)
set a background color; a 2nd parameter is optional to set
a foreground color too
color.fg COLOR (COLOR2)
set a foreground color; a 2nd parameter is optional to set
a background color too
color.preset PRESET
Apply the color set of foreground and background of a given
preset name.
color.echo COLOR|PRESET (COLOR2) TEXT
write a colored text with carriage return and reset colors
The 1st param must be a COLOR(code/ name) for the
foreground or a label of a preset.
The 2nd CAN be a color for the background, but can be
skipped.
Everything behind is text for the output.
color.print COLOR|PRESET (COLOR2) TEXT
see color.echo - the same but without carriage return.
color.reset reset colors
color.set RAWCOLOR (RAWCOLOR2 (... RAWCOLOR_N))
set ansi colors; it can handle multiple color values
---------- Other:
color.blink start blinking text
color.bold start bold text
color.invert start inverted text
color.underline start underline text
VALUES:
COLOR a color; it can be...
- a color keyword, eg black, blue, red, ... for all
known values run 'color.list'
- a value 0..7 to set basic colors 30..37 (or 40..47)
- an ansi color value eg. "30" or "1;42"
- RGB hexcode with '#' as prefix followed by 2 digit
hexcode for red, green and blue eg. "#10404f"
(like css rgb color codes)
PRESET Name of a custom preset; see DEFINE PRESETS below.
RAWCOLOR an ansi color value eg. "30" (black foreground) or
"1;42" (lightgreen background)
DEFINE PRESETS:
A shortcut for a combination of foreground + background color. The label
is part of a bash variable with the prefix 'COLOR_PRESET_'.
The value is a bash array with 2 colors for foreground and background.
See the value description for COLOR above.
SYNTAX:
COLOR_PRESET_<LABEL>=(<FOREGROUND> <BACKGROUND>)
To see all defined presets use 'color.presets'
EXAMPLES:
First you need to source the file $_self.
. $_self
(1)
Show output of the command 'ls -l' in blue
color.fg "blue"
ls -l
color.reset
(2)
show a red error message
color.echo "red" "ERROR: Something went wrong."
(3)
Use a custom preset:
COLOR_PRESET_error=("white" "red")
color.echo "error" "ERROR: Something went wrong."
This defines a preset named "error". "white" is a colorname
for the foreground color, "red" ist the background.
EOH
if [ -n "$NO_COLOR" ]; then
echo -n "INFO: NO_COLOR=$NO_COLOR was set. The coloring functionality is "
if ! color.__usecolor; then
echo "DISBALED."
else
echo "ENABLED (must be 1 to disable)."
fi
echo
else
echo "INFO: NO_COLOR will be respected - but it is not set."
fi
COLOR_DEBUG=$_debug
}
# a little helper: show colors and the color codes
function color.list(){
color.reset
local _debug=$COLOR_DEBUG
COLOR_DEBUG=0
echo
echo "List of colors:"
echo
echo "--------------------------------------------------"
echo "color | foreground | background"
echo "--------------------------------------------------"
for i in "${!COLOR_CODE[@]}"
do
printf "%-15s %4s " $i ${COLOR_CODE[$i]}
color.set "${COLOR_CODE[$i]}"
color.set "40"
printf " Test "
color.set "1;47"
color.set "${COLOR_CODE[$i]}"
printf " Test "
color.reset
printf " %5s " ${BGCOLOR_CODE[$i]}
color.set ${BGCOLOR_CODE[$i]}
printf " Test "
color.reset
echo
done | sort
color.reset
echo "--------------------------------------------------"
echo
COLOR_DEBUG=$_debug
}
# little helper: sow defined presets and its preview
function color.presets(){
local _label
local _value
local _colorvar
local _fg
local _bg
color.reset
local _debug=$COLOR_DEBUG
COLOR_DEBUG=0
if ! set | grep "^COLOR_PRESET_.*=(" >/dev/null; then
echo "INFO: No preset was defined yet."
echo "To set one define shell variables with an array of 2 colors:"
echo " COLOR_PRESET_<LABEL>=(<FOREGROUND> <BACKGROUND>)"
echo "For more help call 'color.help' or see the docs."
else
echo
echo "List of presets:"
echo
echo "---------------------------------------------------------------------"
echo "label | foreground | background | example"
echo "---------------------------------------------------------------------"
set | grep "^COLOR_PRESET_.*=(" | while read -r line
do
_label=$( cut -f 1 -d '=' <<< "$line" | cut -f 3- -d '_')
_example=$( color.print "$_label" "example for peset '$_label'" )
_colorvar="COLOR_PRESET_${_label}"
eval "_fg=\${$_colorvar[0]}"
eval "_bg=\${$_colorvar[1]}"
printf "%-10s | %-12s | %-12s | %-50s\n" "$_label" "${_fg}" "${_bg}" "$_example"
done
echo "---------------------------------------------------------------------"
echo
fi
COLOR_DEBUG=$_debug
}
# ----------------------------------------------------------------------
# FUNCTIONS :: set color
# ----------------------------------------------------------------------
# set background color
# param string backround color 0..7 OR color name eg "black" or a valid color value eg "1;30"
# param string optional: foreground color
function color.bg(){
color.__wd "$FUNCNAME $1"
color.__fgorbg "$1" 4
test -n "$2" && color.fg "$2"
}
# get a color of a preset
# param string name of preset
# param integer array index; 0= foreground; 1= background
function color.__getpresetcolor(){
local _label=$1
local _index=$2
local _colorvar
_colorvar="COLOR_PRESET_${_label}"
eval "echo \${$_colorvar[$_index]}"
}
# set foreground color
# param string foreground color 0..7 OR color name eg "black" or a valid color value eg "1;30"
# param string optional: background color
function color.fg(){
color.__wd "$FUNCNAME $1"
color.__fgorbg "$1" 3
test -n "$2" && color.bg "$2"
}
# set colors of a preset
# param string label of a preet
function color.preset(){
if color.__isapreset "$1"; then
local _colorvar
local _colfg=$( color.__getpresetcolor "$1" 0)
local _colbg=$( color.__getpresetcolor "$1" 1)
color.reset
test -n "$_colfg" && color.__fgorbg "$_colfg" 3
test -n "$_colbg" && color.__fgorbg "$_colbg" 4
else
>&2 echo "ERROR: this value is not a valid preset: $1. See 'color.presets' to see current presets."
fi
}
# ----------------------------------------------------------------------
# reset all colors to terminal default
function color.reset(){
color.__wd "$FUNCNAME"
color.set "0"
}
# start bold text
function color.bold(){
color.__wd "$FUNCNAME"
color.set "1"
}
# start underline text
function color.underline(){
color.__wd "$FUNCNAME"
color.set "4"
}
# start blinking text
function color.blink(){
color.__wd "$FUNCNAME"
color.set "5"
}
# start inverted text
function color.invert(){
color.__wd "$FUNCNAME"
color.set "7"
}
# ----------------------------------------------------------------------
# write ansicode to set color combination
# param string color 1 as ansi value
# param string color N as ansi value
function color.set(){
local _out=
if color.__usecolor; then
for mycolor in $*
do
color.__wd "$FUNCNAME: processing color value '${mycolor}'"
_out+="${mycolor}"
done
color.__wd "$FUNCNAME: output is '\e[${_out}m'"
printf "\e[${_out}m"
else
color.__wd "$FUNCNAME: skipping - coloring is disabled."
fi
}
# ----------------------------------------------------------------------
# FUNCTIONS :: print
# ----------------------------------------------------------------------
# show a colored text WITH carriage return
# param string foreground color as code / name / value
# param string optional: background color as code / name / value
# param string text to print
function color.echo(){
color.__wd "$FUNCNAME $*"
local _param1="$1"
local _param2="$2"
shift 1
shift 1
color.print "$_param1" "$_param2" "$*"
echo
}
# show a colored text without carriage return
# param string foreground color as code / name / value or preset
# param string optional: background color as code / name / value
# param string text to print
function color.print(){
color.__wd "$FUNCNAME $*"
if color.__isacolor "$1"; then
if color.__isacolor "$2"; then
color.fg "$1" "$2"
shift 1
shift 1
else
color.fg "$1"
shift 1
fi
elif color.__isapreset "$1"; then
color.preset "$1"
shift 1
else
>&2 echo -n "ERROR: Wrong color values detected. Command was: colors.print $*"
fi
echo -n "$*"
color.reset
}
# ======================================================================
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment