Welcome to the MacNN Forums.

If this is your first visit, be sure to check out the FAQ by clicking the link above. You may have to register before you can post: click the register link above to proceed. To start viewing messages, select the forum that you want to visit from the selection below.

You are here: MacNN Forums > Software - Troubleshooting and Discussion > macOS > shell script: nested arrays?

shell script: nested arrays?
Thread Tools
Professional Poster
Join Date: Sep 2000
Location: San Francisco
Status: Offline
Reply With Quote
Mar 11, 2010, 09:10 PM
I'm trying to nest some arrays in a shell script, but I can't get it to work. Here's my code:

#!/bin/sh echo "How many periods? " read j for (( i=1; i <= $j; i++ )); do echo $i$j echo "How many spikes in period ${i}?" read spikes echo "What is the duration of the spikes for period ${i}?" read duration echo "What is the interval between spikes for period ${i}?" read interval echo "What is the lag time before the next period?" read lag period=(${spikes},${duration},${interval},${lag}); echo ${period[@]} periods=(${periods[@]} ${period}); done echo ${periods[@]} for ((p=0; p<=${#periods[@]};p++)); do echo "p= ${p}" thisperiod=${periods[$p]} echo "thisperiod = ${thisperiod}" echo "zero element of thisperiod= ${thisperiod[0]}" for ((s=1; s<=${thisperiod[0]};s++)); do echo "s= ${s}" done done
Basically, the last for loop just goes forever. I can't get '${thisperiod[0]} to resolve to an element. It just keeps returning the whole thisperiod array. I'm very confused.

Mac Elite
Join Date: Oct 1999
Location: San Jose, Ca
Status: Offline
Reply With Quote
Mar 12, 2010, 12:47 AM
I believe that this is technically possible... but trying to do things this complex in sh/bash/tcsh/etc is just a headache. When it comes to things like this I switch to a newer language like Perl/Python/Lua/etc. And this is coming from someone who is maintaining a bash script with over 1300 lines (InstaDMG.. I really am trying to switch it over to python... really).
Hal Itosis
Grizzled Veteran
Join Date: Mar 2004
Status: Offline
Reply With Quote
Mar 13, 2010, 07:17 PM
This will do what you want (more or less). As larkost implies, arrays in Bash are one-dimensional... so a combination of tricks and tradeoffs are needed to pull it off.

A few comments first...
Originally Posted by kman42 View Post
Really should shebang with #!/bin/bash to be true-to-form (sh doesn't do arrays, technically speaking).

Originally Posted by kman42 View Post
Looks nice on paper, and Bash doesn't bark. But -- if/when that gets treated as an array -- it only has one element (e.g., 1,2,3,4). (Else, another choice might be to assign the array elements on the fly, with each of those read operations: read period[0]; read period[1]; read period[2]; read period[3]. But still, putting that array into the periods array would just result in one big array).

Originally Posted by kman42 View Post
echo "zero element of thisperiod= ${thisperiod[0]}
As noted above, the items going into periods (e.g., 1,2,3,4) are not arrays themselves (or at best are single-element arrays). So basically $thisperiod is just a string and therefore ${thisperiod[0]} means nothing (nothing expected anyway). See below for how to still make it functional (as long as real indexing doesn't matter).

I had fun brushing up on (and adding to) my knowledge of array syntax while coming up with this... so here it is:
#!/bin/bash - IFS=$' \t\n' declare -x PATH=/bin:/usr/bin declare -a PERIODS printf "How many periods? " read j for ((i=1; i<=$j; i++)) # Load data... do printf "\n$i/$j:\nHow many spikes in period $i? " read spikes printf "What is the duration of the spikes for period $i? " read duration printf "What is the interval between spikes for period $i? " read interval printf "What is the lag time before the next period? " read lag # Splice the "elements" together, using commas (or some char) as glue: period=$spikes,$duration,$interval,$lag # <--a multi-parameter string. echo "period $i = $period" PERIODS=( "${PERIODS[@]}" "$period" ) # The only real (indexed) array. done echo echo "$j periods: ${PERIODS[@]}" # printf trips up with arrays, must use echo. sleep 1 for ((p=0; p<${#PERIODS[@]}; p++)) # Note that: ${#PERIODS[@]} will likely get the same value as "$j" already is. do printf "\nperiod %d:\n" $(($p+1)) # Unglue the pieces of each period by converting commas into spaces... # and then (instead of using indexes) just iterate over existing data: for s in ${PERIODS[$p]//,/ } # Bash parameter expansion w/substitution do echo "s = $s" done done exit $?
In order for this to work, the items going into each "period" must not contain spaces themselves... else the expansion trick will wind up iterating over more s values than desired. Finally (as also noted by larkost), other languages such as Perl are better built for this sort of thing.


EDIT: on further thought, if one absolutely wanted to have array indexes for that final inner do-loop, then the expansion/substitution could be assigned into a new array...
printf "\nperiod %d:\n" $(($p+1)) # Unglue the pieces of each period (convert the commas into spaces) by # using parameter expansion w/substitution to fabricate a new array... thisPeriod=( ${PERIODS[$p]//,/ } ) for ((s=0; s<${#thisPeriod[@]}; s++)) do echo "data [$p,$s] = ${thisPeriod[$s]}" done
...but the advantage of doing so (if any) is not readily apparent (at least not in terms of that single echo statement given in post #1).
( Last edited by Hal Itosis; Mar 14, 2010 at 02:08 AM. Reason: added 2nd array)
Thread Tools
Forum Links
Forum Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts
BB code is On
Smilies are On
[IMG] code is On
HTML code is Off
Privacy Policy
All times are GMT -4. The time now is 10:51 PM.
All contents of these forums © 1995-2017 MacNN. All rights reserved.
Branding + Design: www.gesamtbild.com
vBulletin v.3.8.8 © 2000-2017, Jelsoft Enterprises Ltd.,