Best way to simulate multidimensional arrays "objects" in BASH?

Progress 11: POSIX or bust

This is a proof of concept for zero knowledge traversal of a BAAML dataset declaring the datatypes of each property with it’s address.

The code below is the initial foundation for replacing perl and RegEx.

#!/usr/bin/env sh

MyData=`cat <<\EOF
	SpaceX
		headquarters
			address:Rocket Road
			city:Hawthorne
			state:California
		links
			website:https://www.spacex.com/
			flickr:https://www.flickr.com/photos/spacex/
			twitter:https://twitter.com/SpaceX
			elon_twitter:https://twitter.com/elonmusk
		name:SpaceX
		founder:Elon Musk
		founded:2002
		employees:8000
EOF`

HuntMode="TabCount"; Level=0; PropName=""

while IFS= read -rn1 Char; do
	if [ "$HuntMode" = "TabSearch" ]; then
		# Look for tabs
		if [ "$Char" = "	" ]; then
			HuntMode="TabCount"
		fi
	fi
	if [ "$HuntMode" = "TabCount" ]; then
		# Count tabs
		if [ "$Char" = "	" ]; then
			(( Level = Level + 1 ))
		else
			# Property name found
			# The property name array needs to be ready to have the next property name appended to it. It needs to be trimmed if the hierarchical $Level is the same or less than the number of property names present.
			if [ "$(( $# - $Level))" -ge 0 ]; then
				for i in $(seq 1 $#); do
					[ "$i" -lt $Level ] && set -- "$@" "$1"
					shift
				done
			fi
			HuntMode="PropertyName"
		fi
	fi
	if [ "$HuntMode" = "PropertyName" ]; then
		# Gather property name
		if [ "$Char" = ":" ] || [ "$Char" = "" ]; then
			# Property name gathered, add to array
			set -- "$@" "${PropName}"
			# Reset state back to tab search skipping value
			HuntMode="TabSearch"; Level=0; PropName=""
			# Declare the property name and type for the demo
			if [ "$Char" = ":" ]; then # Property is a value pair
				echo "\"$@\" is a value pair"
			elif [ "$Char" = "" ]; then # Property is a an object
				echo "\"$@\" is an object"
			fi
		else
			# Read property name
			PropName="${PropName}${Char}"
		fi
	fi
done <<< $MyData

Output:

“SpaceX” is an object
“SpaceX headquarters” is an object
“SpaceX headquarters address” is a value pair
“SpaceX headquarters city” is a value pair
“SpaceX headquarters state” is a value pair
“SpaceX links” is an object
“SpaceX links website” is a value pair
“SpaceX links flickr” is a value pair
“SpaceX links twitter” is a value pair
“SpaceX links elon_twitter” is a value pair
“SpaceX name” is a value pair
“SpaceX founder” is a value pair
“SpaceX founded” is a value pair
“SpaceX employees” is a value pair

Takeaway:

POSIX Shell doesn’t have arrays but it can use arguments like an array.

I needed something to keep track of property names as the script moved through the hierarchy and this fit the bill. shift makes it easy to remove arguments from the beginning but the tricky part was removing arguments from the end.

I came up with this:

set Setting 5 new array "elements here"

# Remove 2 arguments from the end
X=2
for i in $(seq 1 $#); do
	[ "$i" -le $(($# - $X)) ] && set -- "$@" "$1"
	shift
done

echo $@

Output:

Setting 5 new

  • It loops as many times as there are arguments.
  • Each loop takes the 1st argument and copies it to the end, then deletes the 1st argument.
  • Unless… it’s within $X of the argument total in which case it doesn’t copy the 1st argument, it just deletes it.
  • When the loop ends, all arguments are back in their original order and $X arguments are gone from the end.

Cheat sheet for more commands here: