#!/bin/bash # colorize-url - apply ANSI colors to URL components # # Usage: # colorize-url # colorize-url -h # # Parses a URL into protocol, hostname, path, query parameters, and fragment, # then prints it with each component in a distinct color for readability # Query string keys and values are colored separately _colorize_url() ( local SCRIPT_NAME; SCRIPT_NAME="$(basename "${BASH_SOURCE[0]}")" case "${BASH_SOURCE[0]}" in /dev/*|/proc/*) SCRIPT_NAME="" ;; esac case "$SCRIPT_NAME" in ""|bash|sh|zsh|dash) SCRIPT_NAME="colorize-url" ;; esac #/# Internal Functions #\# # Prints the nonprinting character for the requested ANSI color _color() { local base=30 # regular colors start here local bright_base=90 # bright (bold in some terminals) colors start here # Get starting value based on whether or not bright variant was requested if [ "$1" = 'bright' ]; then local color_code=$bright_base && shift else local color_code=$base fi # Convert color name to ANSI offset local offset case "$1" in black) offset=0 ;; red) offset=1 ;; green) offset=2 ;; yellow) offset=3 ;; blue) offset=4 ;; magenta) offset=5 ;; cyan) offset=6 ;; white) offset=7 ;; reset) printf '\033[m'; return 0 ;; # reset to terminal default *) _error "Invalid color '$1'"; return 1 ;; esac color_code=$((color_code + offset)) printf '\033[%sm' "$color_code" } # Returns 0 if $1 contains $2 _contains() { local string="$1" local substring="$2" # If string contains substring, it will be deleted and the two strings will no longer match [ "${string#*"$substring"}" != "$string" ] } _error() { echo "[ERR][$SCRIPT_NAME] $*" >&2; } # Clean up internal functions when finished #\# Internal Functions #/# _show_help() { local s; [ -t 1 ] && s=$'\033[4m' local r; [ -t 1 ] && r=$'\033[24m' echo "NAME" echo " $SCRIPT_NAME - apply ANSI colors to URL components" echo "SYNOPSIS" echo " $SCRIPT_NAME ${s}url${r}" echo " $SCRIPT_NAME -h" echo "DESCRIPTION" echo " Parses a URL into protocol, hostname, path, query parameters, and fragment," echo " then prints it with each component in a distinct color for readability." echo " Query string keys and values are colored separately." echo "OPTIONS" echo " -h, --help Show this help message" echo "EXIT STATUS" echo " 0 Success" echo " 2 Usage error (no URL provided, or URL missing protocol)" } case "$1" in -h|--help) _show_help; return 0 ;; esac #/# Color Control Characters #\# local c_protocol; c_protocol="$(_color bright black)" # gray local c_hostname; c_hostname="$(_color bright white)" local c_path; c_path="$(_color bright cyan)" local c_key; c_key="$(_color bright yellow)" local c_value; c_value="$(_color green)" local c_slash; c_slash="$(_color white)" local c_question; c_question="$(_color black)" local c_equals; c_equals="$(_color white)" local c_and; c_and="$(_color black)" local c_hash; c_hash="$(_color white)" local c_reset; c_reset="$(_color reset)" #\# Color Control Characters #/# # User input local url="$1" if [ -z "$url" ]; then _error "No URL provided. Run \`$SCRIPT_NAME -h\` for usage" return 2 fi # Require an explicit protocol (e.g. https://). Parsing relies on '://' as # the first delimiter; without it, results are silently wrong if ! _contains "$url" "://"; then _error "URL must include protocol (e.g. https://...). Run \`$SCRIPT_NAME -h\` for usage" return 2 fi # Check whether URL has a path, query string, and/or hash (fragment) local has_path local has_query local has_hash # If the URL contains a slash not prefixed by another one, there is a path echo "$url" | grep -q -e '[^/]\/[^/]' -e '[^/]\/$' && has_path=1 local _before_hash="${url%%'#'*}" # Check for query only before the hash _contains "$_before_hash" "?" && has_query=1 _contains "$url" "#" && has_hash=1 # Split the URL into parts # Intermediate variables prefixed with underscore local protocol="${url%%'://'*}" local _url_no_protocol="${url#*'://'}" local hostname='' local path='' local query='' local hash='' # Extract pieces based on available delimiters (path, query, hash) if [ "$has_path" ]; then hostname="${_url_no_protocol%%'/'*}" local _path_query_hash="${_url_no_protocol#*/}" # Everything after the first slash if [ "$has_query" ] && [ "$has_hash" ]; then local _query_hash="${_path_query_hash#*'?'}" path="${_path_query_hash%%'?'*}" query="${_query_hash%%'#'*}" hash="#${_query_hash#*'#'}" elif [ "$has_query" ]; then path="${_path_query_hash%%'?'*}" query="${_path_query_hash#*'?'}" elif [ "$has_hash" ]; then path="${_path_query_hash%%'#'*}" hash="#${_path_query_hash#*'#'}" else path="$_path_query_hash" fi # Pathless URLs else if [ "$has_query" ]; then hostname="${_url_no_protocol%%'?'*}" elif [ "$has_hash" ]; then hostname="${_url_no_protocol%%'#'*}" else hostname="${_url_no_protocol}" fi local _after_hostname="${_url_no_protocol#"$hostname"}" if [ "$has_query" ] && [ "$has_hash" ]; then local _query_part="${_after_hostname#*\?}" local _hash_part="${_query_part#*#}" query="${_query_part%%'#'*}" hash="#$_hash_part" elif [ "$has_query" ]; then query="${_after_hostname#*\?}" elif [ "$has_hash" ]; then local _hash_part="${_after_hostname#*#}" hash="#$_hash_part" fi fi # Colorize query string key-value pairs if applicable if [ "$has_query" ]; then local query_params; IFS='&' read -r -a query_params <<<"$query" local colorized_query for param in "${query_params[@]}"; do local param_parts; IFS='=' read -r -a param_parts <<<"$param" local key="${param_parts[0]}" local value="${param_parts[1]}" if [ "${value}" ]; then colorized_query+="$c_and&$c_key${key}$c_equals=$c_value${value}$c_reset" else # Include an equals sign only if one was used in the original URL # e.g. see `k2`: `?k1=v1&k2=&k3=v3` vs `?k1=v1&k2&k3=v3` if _contains "$url" "$key="; then # if echo "$query" | grep -q "\(^\|&\)$key="; then colorized_query+="$c_and&$c_key${key}$c_equals=$c_reset" else colorized_query+="$c_and&$c_key${key}$c_reset" fi fi done # Replace leading ampersand with a question mark (&k1=v1&k2=v2 -> ?k1=v1&k2=v2) query="$c_question?${colorized_query#"$c_and&"}" # leading "?" fi # Colorize path components and slashes if [ "$has_path" ]; then local colorized_slash="$c_slash/$c_path" path="$colorized_slash${path//"/"/"$colorized_slash"}" fi # Finish colorizing and reassemble URL local colorized_url="$c_protocol${protocol}://$c_hostname${hostname}${path}${query}${c_hash}${hash}$c_reset" echo "$colorized_url" ) _colorize_url "$@" __colorize_url_rc=$? unset -f _colorize_url if [ -n "${BASH_SOURCE[0]}" ] && [ "${BASH_SOURCE[0]}" != "$0" ]; then eval "unset __colorize_url_rc; return $__colorize_url_rc" fi eval "unset __colorize_url_rc; exit $__colorize_url_rc"