Skip to content

Instantly share code, notes, and snippets.

@mkyrychenko
Last active January 8, 2021 12:54
Show Gist options
  • Save mkyrychenko/8448296e5c980ab6ea05737079e2d5aa to your computer and use it in GitHub Desktop.
Save mkyrychenko/8448296e5c980ab6ea05737079e2d5aa to your computer and use it in GitHub Desktop.
Colourized logger for shell scripts in the terminal (compatible with POSIX shell)
#!/usr/bin/env sh
# set term colors
# read more at:
# https://misc.flogisoft.com/bash/tip_colors_and_formatting
# http://ascii-table.com/ansi-escape-sequences-vt-100.php
#
# font attributes to use in the terminal with '\e[_%ATTR%_m' (_%ATTR%_ should be replaced with integer)
# e.g. $ echo -e "\e[3m test \e[0m"
# outputs string "test" in italic
#
# explanation of \e[1m
# echo -e - The -e option means that escaped (backslashed) strings will be interpreted
# \e - escaped sequence represents beginning/ending of the style
# 1 - font attribute "bold" (see above)
# lowercase "m" - indicates the end of the sequence
# NOTE: "\e" can be also replaced with ANSI code of letter "e" - "\033", e.g. "\e[0m" equals "\033[0;0m"
# shellcheck disable=SC2034 # suppress warning about unused variables, as they used in the downstream scripts
FONT_BLACK='\e[0;30m' # black
FONT_GRAY='\e[1;30m' # gray
FONT_RED='\e[0;31m' # red
FONT_LIGHT_RED='\e[1;31m' # light red
FONT_GREEN='\e[0;32m' # green
FONT_LIGHT_GREEN='\e[1;32m' # light green
FONT_BROWN='\e[0;33m' # brown
FONT_YELLOW='\e[1;33m' # yellow
FONT_BLUE='\e[0;34m' # blue
FONT_LIGHT_BLUE='\e[1;34m' # light blue
FONT_PURPLE='\e[0;35m' # purple
FONT_LIGHT_PURPLE='\e[1;35m' # light purple
FONT_CYAN='\e[0;36m' # cyan
FONT_LIGHT_CYAN='\e[1;36m' # light cyan
FONT_LIGHT_GRAY='\e[0;37m' # light gray
FONT_WHITE='\e[1;37m' # white
FONT_BACKGROUND_BLACK='\e[40;m'
FONT_BACKGROUND_RED='\e[41;m'
FONT_BACKGROUND_GREEN='\e[42;m'
FONT_BACKGROUND_YELLOW='\e[43;m'
FONT_BACKGROUND_BLUE='\e[44;m'
FONT_BACKGROUND_MAGENTA='\e[4;5m'
FONT_BACKGROUND_CYAN='\e[46;m'
FONT_BACKGROUND_WHITE='\e[47;m'
FONT_BACKGROUND_BRIGHT_BLACK='\e[40;1m'
FONT_BACKGROUND_BRIGHT_RED='\e[41;1m'
FONT_BACKGROUND_BRIGHT_GREEN='\e[42;1m'
FONT_BACKGROUND_BRIGHT_YELLOW='\e[43;1m'
FONT_BACKGROUND_BRIGHT_BLUE='\e[44;1m'
FONT_BACKGROUND_BRIGHT_MAGENTA='\e[45;1m'
FONT_BACKGROUND_BRIGHT_CYAN='\e[46;1m'
FONT_BACKGROUND_BRIGHT_WHITE='\e[47;1m'
# font attribute modifiers can be combined with color modifiers
# e.g. echo -e "${FONT_BOLD}${FONT_BACKGROUND_BRIGHT_RED}${FONT_LIGHT_BLUE}test${FONT_RESET}"
# will output bold light blue "test" on the bright red background
FONT_BOLD='\e[1;m' # bold on
FONT_BOLD_OFF='\e[21;m' # bold off
FONT_DIM='\e[2;m' # dimmed (half-bright)
FONT_ITALIC='\e[3;m' # italic
FONT_UNDERLINE='\e[4;m' # underline on
FONT_UNDERLINE_OFF='\e[24;m' # underline off
FONT_BLINKING='\e[5;m' # blinking ::: WARNING -> VTE (aka GNOME) terminals doesn't support blinking. Only xterm
FONT_REVERSED='\e[7;m' # reversed color and background
FONT_INVISIBLE='\e[8;m' # reversed color and background
FONT_COLOR_RESET='\e[39m\e[39m' # reset color
FONT_RESET='\e[0m' # reset any color and formatting
CLEAR_LINE='\e[2K' # clear line
CLEAR_SCREEN='\e[2J' # clear the screen, move cursor to (0,0)
CLEAR_LINE_END='\e[K' # erase from current cursor position to the end of line
CURSOR_POSITION_SAVE='\e[s' # save cursor position
CURSOR_POSITION_RESTORE='\e[u' # restore cursor position
# [[:digit:]]{0,3}(;[[:digit:]]{0,3})?[Hf] -> absolute cursor position '\e[<L>;<C>H' or '\e[<L>;<C>f, where <L> and <C> (and semicolon between) aren't mandatory
# [[:digit:]]{0,3}[ABCD]) -> relative cursor position '\e[<N>A', '\e[<N>B', '\e[<N>C' or '\e[<N>D', where <N> isn't mandatory
# suK or 2[JK] -> save/restore cursor position, clear display or clear from cursor to the end of line
# [[:digit:]]{1,3}(;|;[[:digit:]]{1,3})?m -> graphics modes (like color, background color, font etc.)
#
# to get rid of escape sequences, use
# sed "s/\[^[[0-9;]*[a-zA-Z]//gi"
___TTY_MODIFIER_PATTERN='\\e\[([[:digit:]]{0,3}(;[[:digit:]]{0,3})?[Hf]|[[:digit:]]{0,3}[ABCD]|[suK]|2[JK]|[[:digit:]]{1,3}(;|;[[:digit:]]{1,3})?m)'
___tokenize_escaped_args() {
# shellcheck disable=SC1004 ## suppress warning about literal new line within single quotes
split=$(
sed -E -e 's@('"${___TTY_MODIFIER_PATTERN}"')+@\
&\
@g' <<EOF
${1}
EOF
)
result=$(
sed -E \
-e '/./,$!d' \
-e '/^('"${___TTY_MODIFIER_PATTERN}"')+$/! s/^.*$/\%s/' <<EOF
${split}
EOF
)
echo "${result}" | tr -d '\n'
}
___tokenize_strings() {
# shellcheck disable=SC2005 ## suppress warning about useless echo
split="$(echo "$(
# shellcheck disable=SC1004 ## suppress warning about literal linefeed with backslash inside of single quotes
sed -E \
-e 's/\\n/\\\\n/g' \
-e 's/'"${___TTY_MODIFIER_PATTERN}"'/\
/g' <<EOF
${1}
EOF
)" | sed -E -e '/^$/d')"
echo "${split}" | sed -e 's/^..*$/"&"/' | tr '\n' ' '
}
___type_on_screen() {
pattern=$1
blocks=$2
eval "set -- ${blocks}"
# shellcheck disable=SC2059 # suppress warning about usage of variables in the "printf FORMAT", as we generate the FORMAT
printf "${pattern}\n" "${@}"
}
write() {
text=$1
___type_on_screen "$(___tokenize_escaped_args "${text}")" "$(___tokenize_strings "${text}")"
}
# puts the cursor at line L and column C and type the text and after that brings cursor to previous position
# '\e[<L>;<C>H' OR '\e[<L>;<C>f' - puts the cursor at line <L> and column <C>
#
# usage
# write_at_absolute_cursor 10 20 "${FONT_BOLD}${FONT_BACKGROUND_RED}${FONT_WHITE}test${FONT_RESET}"
# types bold white "test" with red background on the 10th line and 20th column of current terminal screen
write_at_absolute_cursor() {
line=$1
column=$2
text=$3
___type_on_screen "\e[${line};${column}f$(___tokenize_escaped_args "${text}")" "$(___tokenize_strings "${text}")"
}
# moves the cursor relatively from current position by number of lines and / or columns
# negative value - on the left or up, positive - on the right or down
# '\e[<N>A' - move the cursor up N lines
# '\e[<N>B' - move the cursor down N lines
# '\e[<N>C' - move the cursor forward N columns
# '\e[<N>D' - move the cursor backward N columns
#
# usage
# write_at_relative_cursor -10 5 "${FONT_BOLD}${FONT_BACKGROUND_RED}${FONT_WHITE}test${FONT_RESET}"
# types bold white "test" with red background 10 lines upper and 5 lines on the right from current cursor position
write_at_relative_cursor() {
x_move=$1
y_move=$2
text=$3
if [ "${x_move}" -lt 0 ]; then
text="\e[$(printf '%s' "${x_move}" | tr -d '-')A${text}"
elif [ "${x_move}" -gt 0 ]; then
text="\e[${x_move}B${text}"
fi
if [ "${y_move}" -lt 0 ]; then
text="\e[$(printf '%s' "${y_move}" | tr -d '-')D${text}"
elif [ "${y_move}" -gt 0 ]; then
text="\e[${y_move}C${text}"
fi
___type_on_screen "${CURSOR_POSITION_SAVE}$(___tokenize_escaped_args "${text}")${CURSOR_POSITION_RESTORE}" "$(___tokenize_strings "${text}")"
}
#!/usr/bin/env sh
# import colors helper script
. terminal-fonts.sh
# system logger
LOGGER="$(command -v logger)"
HOST="${HOST:-$(hostname)}"
USER="${USER:-$(id -nu)}"
___log_generic() {
logdate=$(date '+%Y-%m-%d %H:%M:%S')
logproc=$$
logmessage="$logdate ${HOST} [${logproc}]: ${1} [${FONT_PURPLE}${USER}${FONT_RESET}] ${2}"
write "${logmessage}"
${LOGGER} "${2}"
}
log_debug() {
if [ "${DEBUG}" = enabled ]; then
___log_generic "${FONT_BOLD}DEBUG${FONT_RESET}" "$@"
fi
}
log() {
___log_generic "${FONT_BOLD}${FONT_BLUE} INFO${FONT_RESET}" "$@"
}
log_warn() {
___log_generic "${FONT_BOLD}${FONT_YELLOW} WARN${FONT_RESET}" "$@"
}
die() {
___log_generic "${FONT_BOLD}${FONT_RED}ERROR${FONT_RESET}" "$@"
exit 1
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment