I am using bash version 5.1.16
I am trying to write a bash script that creates a dynamic number of arrays, each with a dynamic name that will hold some element parts from a declared array. I am attempting to split a single array into multiple arrays that will hold an equal number of elements.
dec_array = (abc10 def2 ghi333 jkl mno51 pqr_6) # this array will always have some multiple of three elements but variable in the number of elements
num_elem=3
num_arr=0 # will eventually incr and loop once this works
for ((i=0; i < ${#dec_array[@]}; i+=num_elem)); do
part=( "${dec_array[@]:i:num_elem}" )
echo "Elements in first example: ${part[*]}"
dyn_array_${num_arr}=( "${dec_array[@]:i:num_elem}" )
echo "Elements in second example: $dyn_array_${num_arr}[*]}"
done
output:
Elements in first example: abc10 def2 ghi333
Elements in first example: jkl mno51 pqr_6
syntax error near unexpected token `"${dec_array[@]:i:num_elem}"'
` dyn_array_${num_arr}=( "${dec_array[@]:i:num_elem}" )'
I need to be able to split dec_array into multiple dyn_array_n, e.g.,
dyn_array_0: abc10 def2 ghi333
dyn_array_1: jkl mno51 pqr_6
What is the correct syntax to accomplish creating a dynamic number of arrays with dynamic names?
With recent version of bash:
echo $BASH_VERSION
5.1.4(1)-release
There is at least 3 differents ways for this:
declare -a
directly: declare -a "prefix_${midlepart}_suffix[variableIndex]=Content"
printf
in same way: printf -v "prefix_${midlepare}_suffix[variableIndex]" '%s' "Some string"
declare -n this="prefix_${midlepare}_suffix";this[variableIndex]=Content
eval
, but ...declare
simply:dec_array=(abc10 def2 ghi333 jkl mno51 pqr_6 stu_XYZ)
num_elements=${#dec_array[@]}
elements_per_array=3
for ((i=0;i<num_elements;i+=elements_per_array)); do
declare -a "dyn_array_$(( i / elements_per_array + 1
))=(${dec_array[*]:i:elements_per_array})"
done
declare -p ${!dyn_array_*}
declare -a dyn_array_1=([0]="abc10" [1]="def2" [2]="ghi333")
declare -a dyn_array_2=([0]="jkl" [1]="mno51" [2]="pqr_6")
declare -a dyn_array_3=([0]="stu_XYZ")
dec_array=(abc10 def2 ghi333 jkl mno51 pqr_6 stu_XYZ hello\ world.)
num_elements=${#dec_array[@]}
elements_per_array=3
for ((i=0;i<num_elements;i+=elements_per_array)); do
part=("${dec_array[@]:i:elements_per_array}")
declare -a "dyn_array_$(( i / elements_per_array + 1 ))=(${part[*]@Q})"
done
declare -p ${!dyn_array_*}
declare -a dyn_array_1=([0]="abc10" [1]="def2" [2]="ghi333")
declare -a dyn_array_2=([0]="jkl" [1]="mno51" [2]="pqr_6")
declare -a dyn_array_3=([0]="stu_XYZ" [1]="hello world.")
answer=42
declare -a "dyn_arr_$((2*21))+=(foo)"
declare -a "dyn_arr_$((84/2))+=(bar\ baz)"
declare -a "dyn_arr_$answer+=($'Hello\nworld..\n')"
declare -a "dyn_arr_$((12))+=({A..I})"
Then for quick showing content of all arrays:
declare -p ${!dyn_arr_*}
declare -a dyn_arr_12=([0]="A" [1]="B" [2]="C" [3]="D" [4]="E" [5]="F" [6]="G" [
7]="H" [8]="I")
declare -a dyn_arr_42=([0]="foo" [1]="bar baz" [2]=$'Hello\nworld..\n')
matrix
:for i in {0..5}; do
a=($((RANDOM%2)){,,,,})
declare -a "mat_$i=(${a[*]@Q})
done
declare -p ${!mat_*}
declare -a mat_0=([0]="1" [1]="0" [2]="1" [3]="0" [4]="1" [5]="0")
declare -a mat_1=([0]="0" [1]="0" [2]="0" [3]="0" [4]="1" [5]="1")
declare -a mat_2=([0]="0" [1]="1" [2]="1" [3]="0" [4]="1" [5]="1")
declare -a mat_3=([0]="0" [1]="0" [2]="1" [3]="1" [4]="1" [5]="1")
declare -a mat_4=([0]="1" [1]="0" [2]="1" [3]="0" [4]="1" [5]="1")
declare -a mat_5=([0]="0" [1]="1" [2]="0" [3]="1" [4]="1" [5]="0")
. <(echo "printf ' | %s |\n'" \"\$\{mat_{0..5}\[\*\]\}\")
| 1 0 1 0 1 0 |
| 0 0 0 0 1 1 |
| 0 1 1 0 1 1 |
| 0 0 1 1 1 1 |
| 1 0 1 0 1 1 |
| 0 1 0 1 1 0 |
printf
:dec_array=(abc10 def2 ghi333 jkl mno51 pqr_6 stu_XYZ hello\ world.)
num_elements=${#dec_array[@]}
elements_per_array=3
for ((i=0;i<num_elements;i+=elements_per_array));do
for ((l=0;l<$elements_per_array;l++));do
(( i+l >num_elements )) && break
printf -v "dyn_array_$(( i / elements_per_array + 1 ))[l]" "%s" "${dec_array[i+l]}"
done
done
declare -p ${!dyn_*}
declare -a dyn_array_1=([0]="abc10" [1]="def2" [2]="ghi333")
declare -a dyn_array_2=([0]="jkl" [1]="mno51" [2]="pqr_6")
declare -a dyn_array_3=([0]="stu_XYZ" [1]="hello world.")
for i in {0..5}; do
for l in {0..5}; do
printf -v randomLetter %03o $((RANDOM%26+65))
printf -v "mat_$i[l]" %b \\$randomLetter
done
done
declare -p ${!mat_*}
could render something like:
declare -a mat_0=([0]="W" [1]="V" [2]="P" [3]="V" [4]="E" [5]="O")
declare -a mat_1=([0]="Y" [1]="C" [2]="X" [3]="L" [4]="U" [5]="C")
declare -a mat_2=([0]="P" [1]="C" [2]="J" [3]="W" [4]="U" [5]="A")
declare -a mat_3=([0]="R" [1]="M" [2]="V" [3]="H" [4]="W" [5]="R")
declare -a mat_4=([0]="S" [1]="U" [2]="E" [3]="U" [4]="X" [5]="F")
declare -a mat_5=([0]="U" [1]="F" [2]="G" [3]="C" [4]="E" [5]="I")
dec_array=(abc10 def2 ghi333 jkl mno51 pqr_6 stu_XYZ $'foo bar\nbaz')
num_elements=${#dec_array[@]}
elements_per_array=3
for ((i=0;i<num_elements;i+=elements_per_array)); do
declare -n crtArry=dyn_array_$(( i / elements_per_array + 1 ))
crtArry=("${dec_array[@]:i:elements_per_array}")
done
declare -p ${!dyn_array_*}
declare -a dyn_array_1=([0]="abc10" [1]="def2" [2]="ghi333")
declare -a dyn_array_2=([0]="jkl" [1]="mno51" [2]="pqr_6")
declare -a dyn_array_3=([0]="stu_XYZ" [1]=$'foo bar\nbaz')
for i in {0..5}; do
declare -n crtArry=mat_$i
crtArry=($((RANDOM%2)){,,,,})
done
declare -p ${!mat_*}
declare -a mat_0=([0]="1" [1]="0" [2]="1" [3]="1" [4]="0")
declare -a mat_1=([0]="0" [1]="0" [2]="0" [3]="0" [4]="1")
declare -a mat_2=([0]="1" [1]="1" [2]="0" [3]="0" [4]="1")
declare -a mat_3=([0]="1" [1]="1" [2]="1" [3]="0" [4]="1")
declare -a mat_4=([0]="1" [1]="0" [2]="0" [3]="1" [4]="0")
declare -a mat_5=([0]="1" [1]="1" [2]="1" [3]="0" [4]="0")
Nameref is useful for functions:
define_dyn() {
local -n this=dyn_array_$1
shift
this=("$@")
}
for ((i=0;i<num_elements;i+=elements_per_array)); do
define_dyn $((i/elements_per_array+1)) "${dec_array[@]:i:elements_per_array}"
done
declare -p ${!dyn_array*}
declare -a dyn_array_1=([0]="abc10" [1]="def2" [2]="ghi333")
declare -a dyn_array_2=([0]="jkl" [1]="mno51" [2]="pqr_6")
declare -a dyn_array_3=([0]="stu_XYZ" [1]=$'foo bar\nbaz')
get_dyn_elem() {
local -n this=dyn_array_$1
echo "${this[$2]}"
}
get_dyn_elem 3 1
foo bar
baz
With bash 4.3+
we can use nameref's to dynamically create variables (including arrays) on the fly.
There are a few ways to slice-n-dice this; for this answer I'll figure out the number of new arrays up front and then as we traverse the dec_array[]
array we'll fill in the new arrays.
#############
# +1 element (stu_XYZ) to demonstrate when element count != multiple of 3:
dec_array=(abc10 def2 ghi333 jkl mno51 pqr_6 stu_XYZ)
num_elements="${#dec_array[@]}"
elements_per_array=3
#############
# we want ceiling(num_elements / elements_per_array):
num_arrays="$(( (num_elements + elements_per_array - 1) / elements_per_array ))"
index=0
for ((i=1; i<=num_arrays; i++))
do
declare -n _arr="dyn_array_$i" # nameref
_arr=() # initialize the new array
for ((j=1; j<=elements_per_array; j++))
do
(( index >= num_elements)) && break 2 # once we run out of elements we break out of both for loops
_arr+=( "${dec_array[index]}" ) # append current element to _arr[]
(( index++ ))
done
done
Verifying results and showing how to access the arrays later in your script:
for ((i=1; i<=num_arrays; i++))
do
declare -n _arr="dyn_array_$i"
echo "############### ${!_arr}"
typeset -p "${!_arr}" # verify results
for ((j=0; j<${#_arr[@]}; j++)) # loop through each new array
do
echo "dyn_array_$i[$j] = ${_arr[j]}" # "${_arr[j]}" ==> individual element reference
done
done
NOTE: the 2nd echo
is printing 4 strings: dyn_array_$i[
+ $j
+ ] =
+ ${_arr[j]}
This generates:
############### dyn_array_1
declare -a dyn_array_1=([0]="abc10" [1]="def2" [2]="ghi333")
dyn_array_1[0] = abc10
dyn_array_1[1] = def2
dyn_array_1[2] = ghi333
############### dyn_array_2
declare -a dyn_array_2=([0]="jkl" [1]="mno51" [2]="pqr_6")
dyn_array_2[0] = jkl
dyn_array_2[1] = mno51
dyn_array_2[2] = pqr_6
############### dyn_array_3
declare -a dyn_array_3=([0]="stu_XYZ")
dyn_array_3[0] = stu_XYZ
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With