Skip to content

Instantly share code, notes, and snippets.

@AlexAtkinson
Last active December 4, 2025 05:31
Show Gist options
  • Select an option

  • Save AlexAtkinson/cba46af65237291a307835be007072c8 to your computer and use it in GitHub Desktop.

Select an option

Save AlexAtkinson/cba46af65237291a307835be007072c8 to your computer and use it in GitHub Desktop.
printFancyHeader.sh
#!/usr/bin/env bash
# shellcheck disable=2183
# GIST NOTES
# v1 is here: https://gist.github.com/AlexAtkinson/49078eb9a2dcff17b28371eb92964cca
# Clean Test - run:
# NOTE: The hash in the URL below should match this gist. <<< VERIFY THIS
# docker run -e LANG="C.UTF-8" -e LC_ALL="C.UTF-8" -it debian /bin/bash
# apt update && apt -y install curl
# source <(curl -s https://gist.githubusercontent.com/AlexAtkinson/cba46af65237291a307835be007072c8/raw/printFancyHeader.sh)
# for opt in " " "-e"; do for i in 0 $(($(tput cols) / 8)) $(($(tput cols) / 8 +1)) $(($(tput cols) / 2)) $(($(tput cols) +10)); do for t in "H" "XX"; do printFancyHeader -w $i -g -t "$t" "$opt" ; done; done; done
# for opt in " " "-e"; do printFancyHeader -t "$(printf '%*s' "$(( $(tput cols) +10 ))" | sed "s/ /X/g")" "$opt"; done
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# Prints a fancy section header.
# See help menu for details.
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
function printFancyHeader() {
help() {
cat << EOF
Print a fancy heading.
Use: ${0##*/} [-w <int>] [-f <bool>] [-v <char>] [-h <char>] [-i <char>] [-t <string(s)>]
-w INT The width of the header Default: Full Screen
Minimum: Title + 8
Note: If odd, this program will subtract 1.
-v CHAR Vertical border character Default: \#
-h CHAR Horizontal border character Default: =
-i CHAR Internal fill character Default: -
-t TITLE "Title Text" (double quoted) Default: Hello World!
-f Fill Default: true
-g Gutters Default: false
-e Emoji Mode Default: Disabled
Fullwidth/thick (DOUBLE SPACE) emoji's only.
Auto-transforms:
[a-zA-Z0-9]( :;<>?!#$&()*+-_~|{}@[]^)
Some characters require escapes(\).
🛸 https://www.amp-what.com/unicode/search/fullwidth 🛸
NOTE: Some characters require escaping.
Examples:
printFancyHeader -w 20 -t Cool Title
printFancyHeader -w 60 -v \* -h \~ -i . -t "Cool Title" -f false
printFancyHeader -v 🐵 -h 🛻 -i 🌭 -t "Monkey Business" -e -g -w $(($(tput cols)/2))
printFancyHeader -t "[ Hello World ]" -e -w 44
EOF
}
visual_width() {
local str width char char_bytes
str="$1"
if width=$(echo -n "$str" | wc -L 2>/dev/null); then
echo "$width"
return
fi
local width=0
local i
for ((i=0; i<${#str}; i++)); do
char="${str:$i:1}"
char_bytes=$(echo -n "$char" | wc -c)
if (( char_bytes > 1 )); then # Emoji
((width += 2))
else # ASCII
((width += 1))
fi
done
echo "$width"
}
repeat_to_width() {
local char target_width current_width result char_width count i max_iterations iterations pad_char space_char
char="$1"
target_width="$2"
space_char="$3"
result=""
char_width=$(visual_width "$space_char")
(( char_width == 0 )) && char_width=1
if (( target_width <= 0 )); then
echo ""
return
fi
(( count = target_width / char_width ))
for ((i=0; i<count; i++)); do
result+="$char"
done
current_width=$(visual_width "$result")
max_iterations=10
iterations=0
while (( current_width < target_width && iterations < max_iterations )); do
result+="$space_char"
current_width=$(visual_width "$result")
((iterations++))
done
while (( current_width > target_width && ${#result} > 0 )); do
result="${result::-$char_width}"
current_width=$(visual_width "$result")
done
echo "$result"
}
# Emoji Character Dictionary
declare -rA e_c=(
["a"]="a" ["b"]="b" ["c"]="c" ["d"]="d" ["e"]="e" ["f"]="f" ["g"]="g"
["h"]="h" ["i"]="i" ["j"]="j" ["k"]="k" ["l"]="l" ["m"]="m"
["n"]="n" ["o"]="o" ["p"]="p" ["q"]="q" ["r"]="r" ["s"]="s" ["t"]="t"
["u"]="u" ["v"]="v" ["w"]="w" ["x"]="x" ["y"]="y" ["z"]="z"
["A"]="A" ["B"]="B" ["C"]="C" ["D"]="D" ["E"]="E" ["F"]="F" ["G"]="G"
["H"]="H" ["I"]="I" ["J"]="J" ["K"]="K" ["L"]="L" ["M"]="M"
["N"]="N" ["O"]="O" ["P"]="P" ["Q"]="Q" ["R"]="R" ["S"]="S" ["T"]="T"
["U"]="U" ["V"]="V" ["W"]="W" ["X"]="X" ["Y"]="Y" ["Z"]="Z"
["0"]="0" ["1"]="1" ["2"]="2" ["3"]="3" ["4"]="4"
["5"]="5" ["6"]="6" ["7"]="7" ["8"]="8" ["9"]="9"
[" "]="  " [":"]=":" [";"]=";" ["<"]="<" [">"]=">" ["?"]="?" ["!"]="!"
["#"]="#" ["$"]="$" ["&"]="&" ["("]="(" [")"]=")" ["*"]="*" ["-"]="-"
["+"]="+" ["_"]="_" ["~"]="~" ["|"]="|" ["{"]="{" ["}"]="}" ["@"]="@"
["["]="[" ["]"]="]" ["^"]="^" ["."]=". "
)
local txt width min_width max_width target_width \
vert_c horz_c intr_c ogut_c igut_c space_c \
vert_width ogut_width igut_width space_width \
fill gut emoji \
title_text title_visual_width \
border_width content_width fill_width_l fill_width_r \
horz_border_fill intr_fill intr_half_fill_l intr_half_fill_r \
c char_width padding max_title_width truncated current_width
txt=()
OPTIND=1
while getopts "w:v:h:i:fget:" OPT; do
case "$OPT" in
w) width="$OPTARG" ;;
v) vert_c="$OPTARG" ;;
h) horz_c="$OPTARG" ;;
i) intr_c="$OPTARG" ;;
f) fill="false" ;;
g) gut="true" ;;
e) emoji="true" ;;
t) set -f
IFS=' '
# shellcheck disable=2206
txt=($OPTARG) ;;
:) echo "ERROR: -$OPTARG requires an argument."
help; return 1 ;;
*) help; return 1 ;;
esac
done
shift $((OPTIND-1))
set +f
max_width=$(tput cols) # Maximum box width (terminal width)
width="${width:-$max_width}" # Title box width
fill="${fill:-true}" # Fill option
gut="${gut:-false}" # Gutter option
# Title Text Control
# shellcheck disable=2206
txt=(${txt[@]:-Hello World\!}) # Title text
if [[ "$emoji" == "true" ]]; then # Emoji Transform
c="${txt[*]}"
# shellcheck disable=2207
txt=($(for ((i=0; i<${#c}; i++)); do
printf "%s" "${e_c["${c:$i:1}"]}"
done))
fi
title_text="${txt[*]}"
title_visual_width=$(visual_width "$title_text")
if [[ "$emoji" != "true" ]]; then # Default characters
[[ "$fill" == "false" ]] && intr_c=' '
vert_c="${vert_c:-#}"
horz_c="${horz_c:-=}"
intr_c="${intr_c:--}"
space_c=" "
ogut_c=" "
igut_c=" "
else
[[ "$fill" == "false" ]] && intr_c="${e_c[" "]}"
vert_c="${vert_c:-🛸}"
horz_c="${horz_c:-🛸}"
intr_c="${intr_c:-👽}"
space_c="${e_c[" "]}"
ogut_c="${e_c[" "]}"
igut_c="${e_c[" "]}"
fi
if [[ "$gut" == "false" ]]; then # Gutter
ogut_c="$horz_c"
igut_c="$intr_c"
fi
vert_width=$(visual_width "$vert_c") # Element widths
ogut_width=$(visual_width "$ogut_c")
igut_width=$(visual_width "$igut_c")
space_width=$(visual_width "$space_c")
local min_fill_char_width # Minimum fill
min_fill_char_width=$(visual_width "$intr_c")
padding=$((2 * vert_width + 2 * igut_width + 2 * space_width + 2 * min_fill_char_width))
max_title_width=$((max_width - padding - 3)) # 3 for ellipses. Truncate if too long
if (( title_visual_width > max_title_width )); then
# Truncate and add ellipses
truncated=""
current_width=0
for ((i=0; i<${#title_text}; i++)); do
char="${title_text:$i:1}"
char_width=$(visual_width "$char")
if (( current_width + char_width + 3 > max_title_width )); then
break
fi
truncated+="$char"
((current_width += char_width))
done
[[ "$emoji" == "true" ]] || title_text="${truncated}..."
[[ "$emoji" == "true" ]] && title_text="${truncated}. . . "
title_visual_width=$(visual_width "$title_text")
fi
min_width=$((title_visual_width + padding)) # Box Width
(( width < min_width )) && width=$min_width
(( width > max_width )) && width=$max_width
border_width=$((width - 2 * vert_width - 2 * ogut_width)) # Border Width
content_width=$((width - 2 * vert_width - 2 * igut_width)) # Content row
title_with_spaces_width=$((2 * space_width + title_visual_width)) # Title row
total_fill_width=$((content_width - title_with_spaces_width))
# Ensure minimum width
if (( total_fill_width < 0 )); then
content_width=$((title_with_spaces_width + 2))
width=$((content_width + 2 * vert_width + 2 * igut_width))
border_width=$((width - 2 * vert_width - 2 * ogut_width))
total_fill_width=2
fi
fill_width_l=$((total_fill_width / 2))
fill_width_r=$((total_fill_width - fill_width_l)) # Observes odd widths
# Ensure minimum fill of 1
(( fill_width_l < 1 )) && fill_width_l=1
(( fill_width_r < 1 )) && fill_width_r=1
# Width offset for emoji mode
if [[ "$emoji" == "true" ]]; then
(( content_width%2 )) || (( fill_width_r++ ))
if (( content_width > min_width )); then
(( fill_width_l > 2 )) && (( fill_width_l-- ))
(( fill_width_l > 2 )) && (( fill_width_r++ ))
fi
fi
horz_border_fill=$(repeat_to_width "$horz_c" "$border_width" "$space_c")
intr_fill=$(repeat_to_width "$intr_c" "$content_width" "$space_c")
intr_half_fill_l=$(repeat_to_width "$intr_c" "$fill_width_l" "$space_c")
intr_half_fill_r=$(repeat_to_width "$intr_c" "$fill_width_r" "$space_c")
#echo "w: $width, bw: $border_width, fl: $fill_width_l, fr: $fill_width_r, tfl: $total_fill_width, tl: $title_visual_width"
printf '%s\n' "${vert_c}${ogut_c}${horz_border_fill}${ogut_c}${vert_c}"
printf '%s\n' "${vert_c}${igut_c}${intr_fill}${igut_c}${vert_c}"
printf '%s' "${vert_c}${igut_c}${intr_half_fill_l}"
printf "%b" "\e[01;39m${space_c}${title_text}${space_c}\e[0m"
printf '%s\n' "${intr_half_fill_r}${igut_c}${vert_c}"
printf '%s\n' "${vert_c}${igut_c}${intr_fill}${igut_c}${vert_c}"
printf '%s\n' "${vert_c}${ogut_c}${horz_border_fill}${ogut_c}${vert_c}"
}
@AlexAtkinson
Copy link
Author

EG:
Screenshot_20251127_230702

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment