Test if a variable is an array:
# is_array - Tests whether variable name is an array # Parameters: # name - name of variable to test # Returns: # 0 if an array # 1 otherwise is_array() { # Only takes one argument if [ "${#}" -ne "1" ]; then return 1 fi # Name of the variable to test local name="$1"; shift # Run the declare command against the variable local x=$(declare -p $name 2>/dev/null) # If it errored then the name probably isn't even # a variable if [ "$?" -ne "0" ]; then return 1 else # It is at least declared, test if it is an array if [ "${x:8:2}" == "-a" ]; then return 0 else return 1 fi fi }Push values onto an array:
# array_push - Pushes one or more values onto an array # Parameters: # name - name of the destination array # ... - values to push onto array # Returns: # Nothing array_push() { # Need two or more arguments if [ "${#}" -lt "2" ]; then return fi # Name of the destination array local name="$1"; shift # Number of elements we're going to push local numElements=${#} # Counter local i=0 # Starting position of destination array is the # size of the array eval local start=\$\{\#$name\[\@\]\} # Iterate over the elements supplied, adding them # to the destination array while [ "$i" -lt "$numElements" ]; do pos=$((start+i)) eval $name\[$pos\]=\"$1\"; shift ((i++)) done }Pop from an array:
# array_pop - pops a value from the end of the array # Parameters: # name - name of array # dest - optional name of variable to put value in # Returns: # Nothing array_pop() { # Need at least one argument if [ "${#}" -eq "0" ]; then return fi # Name of the array local name="$1"; shift # If we have a second argument, use it to write value if [ -n "$1" ]; then local dest="$1"; shift else local dest="" fi # Size of the array eval local numElements=\$\{\#$name\[\@\]\} # Index of element is one less than size local index=$((numElements-1)) # Get the value eval local value=\"\$\{$name\[$index\]\}\" if [ -n "$dest" ]; then # Update the specified dest variable eval $dest=\"$value\" else # Simply echo the value echo "$value" fi # Unset from the array eval unset $name\[$index\] }One thing to note is the clash between variable names. E.g. if you wanted to return the value into the name variable, it would be bad to call that variable 'value'. local doesn't help here, best option would probably to use obscure variable names that are unlikely to clash.
Walk an array:
# array_walk - Walks through an array, calling the # callback function for each element # Parameters: # name - name of array to walk # callback - name of callback function # ... - any parameters to be passed to callback # Returns: # Nothing array_walk() { # Name of array to walk over local name="$1"; shift # Name of call back function local callback="$1"; shift # TODO - We should probably check whether callback # is actually a function # Number of elements in array eval local numElements=\$\{\#$name\[\@\]\} # Counter local i=0 # Iterate over the elements in the array while [ "$i" -lt "$numElements" ]; do # Get the value eval value=$\{$name\[$i\]\} # Check whether it is an array is_array $value if [ "$?" -eq "0" ]; then # If so, recursively walk that array array_walk "$value" "$callback" "$@" else # Just a variable, so call the callback # function giving it the args and value $callback "$@" "$value" fi ((i++)) done }Now, a little play with those functions:
declare -a aTest echo First push array_push aTest 'Hello' 'there' 'you' ':)' echo new array is ${aTest[@]} echo declare -a aTest2 echo Second push array_push aTest2 'How' 'are' 'you today?' echo second array is ${aTest2[@]} echo echo Third push to add second array to the first array_push aTest 'aTest2' echo first array is ${aTest[@]} echo echo Defining destination array declare -a aMerged echo defining callback function merge() { local name="$1" local value="$2" # Simple push the value onto the destination array_push "$name" "$value" } echo Doing walk array_walk aTest merge aMerged echo merged array is ${aMerged[@]} echo echo Popping last element of merged array by reference array_pop aMerged val echo Value is $val echo and again array_pop aMerged val echo Value is $val echo merged array is now ${aMerged[@]}Running it gives us:
$ ./arrays2.sh First push new array is Hello there you :) Second push second array is How are you today? Third push to add second array to the first first array is Hello there you :) aTest2 Defining destination array defining callback function Doing walk merged array is Hello there you :) How are you today? Popping last element of merged array by reference Value is you today? and again Value is are merged array is now Hello there you :) How $So that sort of works. Next I should add an array_indexes() and array_values(). Oh, and happy new year :D
Edit:
Added array_pop function.
Nooo is_array returning 0 for true and 1 for false! Bloody sysadmin programmers /rollseyes
ReplyDeleteOh and happy new year from NZ :)
ReplyDeleteHah :) Happy new year to you too! And yeah, sorry about the whole 0/1 thing :D
ReplyDeletehe is actually correct with the 0/1 thing as 0 is defined as true in shell, everything else is false
ReplyDeleteYeah, that's just JC being a developer :)
ReplyDeletePretty good post. I just stumbled upon your blog and wanted to say that I have really enjoyed reading your blog posts. Any way I'll be subscribing to your feed and I hope you post again soon. Big thanks for the useful info. camping mit kind und hund
ReplyDelete