Skip to content

Commit 506a48b

Browse files
committed
bash - add vardump
1 parent 0bd21ba commit 506a48b

File tree

2 files changed

+241
-7
lines changed

2 files changed

+241
-7
lines changed

src/main/bash/aoc.sh

Lines changed: 25 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,15 @@ E_ASSERT_FAILED=99
55
RED='\033[1;31m'
66
NC='\033[0m' # No Color
77

8+
source "$(dirname "$0")/vardump.bash"
9+
810
# assert
911
# If condition false, exit from script with appropriate error message.
1012
# https://tldp.org/LDP/abs/html/debugging.html#ASSERT
1113
#
12-
assert () {
13-
if [ -z "$2" ]; then # Not enough parameters passed to assert() function.
14-
return "$E_PARAM_ERR" # No damage done.
14+
assert() {
15+
if [ -z "$2" ]; then # Not enough parameters passed to assert() function.
16+
return "$E_PARAM_ERR" # No damage done.
1517
fi
1618

1719
local lineno=$2
@@ -23,7 +25,7 @@ assert () {
2325
local msg="$1"
2426
fi
2527
cmd+=" echo \"Assertion failed: \"$msg\"\" >&2;"
26-
cmd+=" echo \"File \"$0\", line $lineno\" >&2;" # Give name of file and line number.
28+
cmd+=" echo \"File \"$0\", line $lineno\" >&2;" # Give name of file and line number.
2729
cmd+=" exit $E_ASSERT_FAILED;"
2830
# else
2931
# return
@@ -47,6 +49,20 @@ _debug() {
4749
return 0
4850
}
4951

52+
_vardump() {
53+
if [ -n "$DEBUG" ]; then
54+
vardump "$*" >&2
55+
fi
56+
return 0
57+
}
58+
59+
_vvardump() {
60+
if [ -n "$DEBUG" ]; then
61+
vardump -v "$*" >&2
62+
fi
63+
return 0
64+
}
65+
5066
_info() {
5167
if [ -n "$DEBUG" ]; then
5268
echo "**** INFO: $*" >&2
@@ -62,14 +78,16 @@ fatal() {
6278
}
6379

6480
TEST() {
65-
OLDIFS="$IFS"; IFS=' ' read -ra cmd <<< "$1"; IFS="$OLDIFS"
81+
OLDIFS="$IFS"
82+
IFS=' ' read -ra cmd <<< "$1"
83+
IFS="$OLDIFS"
6684
local arg="$2[@]"
6785
local lines=("${!arg}")
6886
local expected="$3"
6987
local actual
7088
actual="$("${cmd[0]}" "${cmd[@]:1}" \
7189
<(for line in "${lines[@]}"; do echo "$line"; done))"
7290
local msg="FAILED ${cmd[*]} SAMPLE $2: expected '$expected', got '$actual'"
73-
[ "$actual" != "$expected" ] \
74-
&& fatal "$E_ASSERT_FAILED" "$msg" >&2
91+
[ "$actual" != "$expected" ] &&
92+
fatal "$E_ASSERT_FAILED" "$msg" >&2
7593
}

src/main/bash/vardump.bash

Lines changed: 216 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,216 @@
1+
#!/usr/bin/env bash
2+
#
3+
## Bash library for pretty-printing a variable given by name.
4+
#
5+
# This was inspired by `util.inspect` in Node.js, `p` in or `pp` in ruby,
6+
# `var_dump` in php, etc. This is intended for developers to use for debugging
7+
# - this should not be parsed by a machine nor should the output format be
8+
# considered stable.
9+
#
10+
# Author: Dave Eddy <[email protected]>
11+
# Date: December 18, 2024
12+
# License: MIT
13+
14+
# vardump
15+
#
16+
# Dump the given variable (by name) information and value to stdout.
17+
#
18+
# Usage: vardump [-v] <name>
19+
#
20+
# Example:
21+
#
22+
# ``` bash
23+
# $ declare -A assoc=([foo]=1 [bar]=2)
24+
# $ vardump assoc
25+
# (
26+
# ['foo']='1'
27+
# ['bar']='2'
28+
# )
29+
# $ vardump -v assoc
30+
# --------------------------
31+
# vardump: assoc
32+
# attributes: (A)associative array
33+
# length: 2
34+
# (
35+
# ['foo']='1'
36+
# ['bar']='2'
37+
# )
38+
# --------------------------
39+
# ```
40+
#
41+
# Arguments:
42+
# -v verbose output
43+
# -C [always|auto|never] when to colorize output, defaults to auto
44+
#
45+
vardump() {
46+
# read arguments
47+
local verbose=false
48+
local whencolor='auto'
49+
local OPTIND OPTARG opt
50+
while getopts 'C:v' opt; do
51+
case "$opt" in
52+
C) whencolor=$OPTARG;;
53+
v) verbose=true;;
54+
*) return 1;;
55+
esac
56+
done
57+
shift "$((OPTIND - 1))"
58+
59+
# read variable name
60+
local name=$1
61+
62+
if [[ -z $name ]]; then
63+
echo 'vardump: name required as first argument' >&2
64+
return 1
65+
fi
66+
67+
# optionally load colors
68+
if [[ $whencolor == always ]] || [[ $whencolor == auto && -t 1 ]]; then
69+
local color_green=$'\e[32m'
70+
local color_magenta=$'\e[35m'
71+
local color_rst=$'\e[0m'
72+
local color_dim=$'\e[2m'
73+
else
74+
local color_green=''
75+
local color_magenta=''
76+
local color_rst=''
77+
local color_dim=''
78+
fi
79+
local color_value=$color_green
80+
local color_key=$color_magenta
81+
local color_length=$color_magenta
82+
83+
# optionally print header
84+
if $verbose; then
85+
echo "${color_dim}--------------------------${color_rst}"
86+
echo "${color_dim}vardump: ${color_rst}$name"
87+
fi
88+
89+
# ensure the variable is defined
90+
if ! declare -p "$name" &>/dev/null; then
91+
echo "variable ${name@Q} not defined" >&2
92+
return 1
93+
fi
94+
95+
# get the variable attributes - this will tell us what kind of variable
96+
# it is.
97+
#
98+
# XXX dave says - is this ideal? when the variable is a nameref (using
99+
# `declare -n` or `local -n`) it seems like this method follows the
100+
# nameref, whereas parsing the output of `declare -p <name>` seems to
101+
# correctly give `-n` as the set of arguments used. Perhaps just parse
102+
# the output of `declare -p` from above here instead?
103+
local attrs
104+
IFS='' read -ra attrs <<< "${!name@a}"
105+
106+
# parse the variable attributes and construct a human-readable string
107+
local attributes=()
108+
local attr
109+
local typ=''
110+
for attr in "${attrs[@]}"; do
111+
local s=''
112+
case "$attr" in
113+
a) s='indexed array'; typ='a';;
114+
A) s='associative array'; typ='A';;
115+
r) s='read-only';;
116+
i) s='integer';;
117+
g) s='global';;
118+
x) s='exported';;
119+
*) s="unknown";;
120+
esac
121+
attributes+=("($attr)$s")
122+
done
123+
124+
# optionally print the attributes to the user
125+
if $verbose; then
126+
echo -n "${color_dim}attributes: ${color_rst}"
127+
if [[ -n ${attributes[0]} ]]; then
128+
# separate the list of attributes by a `/` character
129+
(
130+
IFS=/
131+
echo -n "${attributes[*]}"
132+
)
133+
else
134+
echo -n '(none)'
135+
fi
136+
echo
137+
fi
138+
139+
# print the variable itself! we use $typ defined above to format it
140+
# appropriately.
141+
#
142+
# we *pray* the user doesn't use this variable name in their own code or
143+
# we'll hit a circular reference error (THAT WE CAN'T CATCH OURSELVES IN
144+
# CODE - lame)
145+
local -n __vardump_name="$name"
146+
147+
if [[ $typ == 'a' || $typ == 'A' ]]; then
148+
# print this as an array - indexed, sparse, associative,
149+
# whatever
150+
local key value
151+
152+
# optionally print length
153+
if $verbose; then
154+
local length=${#__vardump_name[@]}
155+
printf '%s %s\n' \
156+
"${color_dim}length:${color_rst}" \
157+
"${color_length}$length${color_rst}"
158+
fi
159+
160+
# loop keys and print the data itself
161+
echo '('
162+
for key in "${!__vardump_name[@]}"; do
163+
value=${__vardump_name[$key]}
164+
165+
# safely quote the key name if it's an associative array
166+
# (the user controls the key names in this case so we
167+
# can't trust them to be safe)
168+
if [[ $typ == 'A' ]]; then
169+
key=${key@Q}
170+
fi
171+
172+
# always safely quote the value
173+
value=${value@Q}
174+
175+
printf '\t[%s]=%s\n' \
176+
"${color_key}$key${color_rst}" \
177+
"${color_value}$value${color_rst}"
178+
done
179+
echo ')'
180+
else
181+
# we are just a simple scalar value - print this as a regular,
182+
# safely-quoted, value.
183+
echo "${color_value}${__vardump_name@Q}${color_rst}"
184+
fi
185+
186+
# optionally print the trailer
187+
if $verbose; then
188+
echo "${color_dim}--------------------------${color_rst}"
189+
fi
190+
191+
return 0
192+
193+
}
194+
195+
# if we are run directly (not-sourced) then run through some examples
196+
# shellcheck disable=SC2034
197+
if ! (return &>/dev/null); then
198+
declare s='some string'
199+
declare -r READ_ONLY='this cant be changed'
200+
declare -i int_value=5
201+
202+
declare -a simple_array=(foo bar baz "$(tput setaf 1)red$(tput sgr0)")
203+
declare -a sparse_array=([0]=hi [5]=bye [7]=ok)
204+
declare -A assoc_array=([foo]=1 [bar]=2)
205+
206+
echo 'simple vardump'
207+
for var in s READ_ONLY int_value simple_array sparse_array assoc_array; do
208+
vardump "$var"
209+
done
210+
211+
echo 'verbose vardump'
212+
for var in s READ_ONLY int_value simple_array sparse_array assoc_array; do
213+
vardump -v "$var"
214+
echo
215+
done
216+
fi

0 commit comments

Comments
 (0)