#!/bin/bash # vardump - Bash library for pretty-printing a variable given by name. # Dump given variable(s) (by name) informations and value to stdout. # # Author: Dave Eddy # Date: December 18, 2024 # License: MIT # # Strongly edited by: Felix Hauri # - Avoiding bash loops whenever possible # - Using associative array for colors # - Creating main loop to dump many variable at once # - Using printf instead of echo # - Added attributes toLower and toUpper # - Using array for attribute (was buggy in Dave's version) # - Then print all attributes verbosely without loop # Version: 0.3.12 -- Last update: Thu Jul 15 10:46:12 CEST 2025 # # Arguments: # -v verbose output # -n print variable names # -C [always|auto|never] when to colorize output, defaults to auto # # shellcheck disable=SC2059 # $fStr are built from arrays to avoid bash loops vardump() { local -A Color=( [rst]=$'\e[0m' [dim]=$'\e[2m' [green]=$'\e[38;5;34;1m' [magenta]=$'\e[38;5;127;1m' [orange]=$'\e[38:5:208m') # read arguments local verbose=false printName=false name local whencolor='auto' local OPTIND OPTARG opt while getopts 'C:vn' opt; do case "$opt" in C) whencolor=$OPTARG;; v) verbose=true;; n) printName=true;; *) local oid=$((OPTIND-1)) printf >&2 \ 'Unknown arg: "%s"!\nUsage: %s [-C auto|always|never] [-v]\n'\ "${!oid}" "${FUNCNAME[0]}" return 1;; esac done shift "$((OPTIND - 1))" for name; do if [[ -z $name ]]; then printf >&2 \ '%s: variable name(s) argument(s) missing!\n' "${FUNCNAME[0]}" return 1 fi # optionally load colors if [[ $whencolor != always ]] && [[ $whencolor != auto || ! -t 1 ]]; then local -A Color=() fi Color[value]=${Color[green]} Color[key]=${Color[magenta]} Color[length]=${Color[orange]} # optionally print header $verbose && printf "%s--------------------------\nvardump: %s%s\n" \ "${Color[dim]}" "${Color[rst]}" "$name" # ensure the variable is defined if ! declare -p "$name" &>/dev/null; then printf >&2 \ '%s: variable %q not defined!\n' "${FUNCNAME[0]}" "${name}" return 1 fi # XXX dave says - is this ideal? when the variable is a nameref (using # `declare -n` or `local -n`), this method will follows the nameref!! # Alternatively, we could parse `declare -p` output, but I think this # function is not intended to show reference of a nameref. # Take attributes separted into an array local attrs="${!name@a}" list _verb=false read -ra attrs <<< "${attrs//?/ &}" # optionally print the attributes if $verbose; then printf '%sattributes:%s ' "${Color[dim]}" "${Color[rst]}" if (( ${#attrs[@]} )); then local -A attrDscs=([a]='indexed array' [A]='associative array' [r]='read-only' [i]='integer' [g]='global' [x]='exported' [l]='loLower' [u]='toUpper' ) read -ra list <<<"${attrs[@]/*/\"(&)\$\{attrDscs[&]\}\"}" [[ $SHELLOPTS ]] && [[ -z ${SHELLOPTS//*verbose*} ]] && _verb=true && set +v eval "printf -v list '%s/' ${list[*]}" $_verb && set -v printf '%s\n' "${list%/}" else printf '%s\n' '(none)' fi fi # # we *pray* the user doesn't use this variable name in their own code or # we'll hit a circular reference error (THAT WE CAN'T CATCH OURSELVES IN # CODE - lame) local -n __vardump_name="$name" if [[ ${attrs[*]//[!aA]} ]]; then # print this as an array - indexed, sparse, associative, # whatever local key value # optionally print length if $verbose; then printf '%slength:%s %s%s%s\nvalue: ' "${Color[dim]}"\ "${Color[rst]}" "${Color[length]}" ${#__vardump_name[@]}\ "${Color[rst]}" elif $printName; then printf '%s%s%s=' "${Color[key]}" "$name" "${Color[rst]}" fi # print keys and values, without loop local fStr="\t[${Color[key]}%q${Color[rst]}]=" fStr+="${Color[value]}%%q${Color[rst]}\n" printf -v fStr "$fStr" "${!__vardump_name[@]}" printf -v fStr "$fStr" "${__vardump_name[@]}" printf '(\n%s\n)\n' "$fStr" else # we are just a simple scalar value - print this as a regular, # safely-quoted, value. if $verbose; then printf '%sstring length:%s %s%s%s\n%svalue:%s %s%q%s\n' \ "${Color[dim]}" "${Color[rst]}" "${Color[length]}" \ ${#__vardump_name} "${Color[rst]}" "${Color[dim]}" \ "${Color[rst]}" "${Color[value]}" "${__vardump_name}" \ "${Color[rst]}" elif $printName; then printf '%s%s%s=%s%q%s\n' "${Color[key]}" "$name" \ "${Color[rst]}" "${Color[value]}" "${__vardump_name}" \ "${Color[rst]}" else printf '%s%q%s\n' "${Color[value]}" "${__vardump_name}" \ "${Color[rst]}" fi fi # optionally print the trailer $verbose && printf '%s--------------------------%s\n' \ "${Color[dim]}" "${Color[rst]}" done return 0 } # Exit here if sourced [[ $0 = "${BASH_SOURCE[0]}" ]] || { true; return 0;} # Else, run some examples: set -v declare -u s s='Some string' declare -r READ_ONLY="This can't be changed" declare -i int_value=5 declare -a simple_array=(foo bar baz "$(tput setaf 1)red$(tput sgr0)") declare -ar sparse_array=([0]=hi [5]=bye [7]=ok) declare -Ari assoc_array=([foo]=12 [bar]=34 [mPI]=3141593) : "$s $READ_ONLY $int_value ${simple_array[*]} \ ${sparse_array[*]} ${assoc_array[*]}" echo 'simple vardump (colors=auto)' vardump -C auto s READ_ONLY int_value simple_array sparse_array assoc_array echo 'vardump with names (colors=never)' vardump -C never -n s READ_ONLY int_value simple_array sparse_array assoc_array echo 'verbose vardump (colors=always)' vardump -C always -v s READ_ONLY int_value simple_array sparse_array assoc_array