I have two arrays:
arrayA=(1 2 3) arrayB=(a b c)
and I want to print out one of them using a command line argument, i.e., without any if else.
I tried a few variations on the syntax with no success. I am wanting to do something like this:
ARG="$1"
echo ${array${ARG}[@]}
but I get a “bad substitution” error. How can I achieve this?
Answers:
Thank you for visiting the Q&A section on Magenaut. Please note that all the answers may not help you solve the issue immediately. So please treat them as advisements. If you found the post helpful (or not), leave a comment & I’ll get back to you as soon as possible.
Method 1
Try doing this :
$ arrayA=(1 2 3)
$ x=A
$ var=array$x[@]
$ echo ${!var}
1 2 3
NOTE
- from
man bash(parameter expansion) :
${parameter} The value of parameter is substituted. The braces are required when parameter is a positional parameter with more than onedigit, or when parameter is followed by a character which
is not to be interpreted as part of its name.
* If the first character of parameter is an exclamation point (!), a level of variable indirection is introduced. Bash uses the
value of the variable formed from the rest of parameter as the
name of the variable; this variable is then expanded and that value is used in the rest of the substitution, rather than the value
of parameter itself. This is known as indirect expansion.
* The exceptions to this are the expansions of ${!prefix*} and ${!name[@]} described below. The exclamation point must immediately
follow the left brace in order to introduce indirection.
Method 2
While you can use the indirect access as pointed in another answer, another way (in ksh and Bash 4.3 and newer) would be to use namerefs. Especially in the case of arrays this may be more useful since you can index the array through the nameref and don’t need to put the index in the variable used as the reference.
arr1=(a b c)
arr2=(x y z)
typeset -n p=arr1 # or 'declare -n'
echo "${p[1]}" # prints 'b'
This doesn’t work through the indirect access:
q=arr2
echo "${!q}" # prints 'x', the same as $arr2
echo "${!q[1]}" # doesn't work, it tries to take q[1] as a reference
As a C programmer might put it, ${!q[1]} here acts as if q was an array of pointers, instead of being a pointer to an array.
Method 3
This took a lot of trial and error but eventually worked.
I took some inspiration from Youness. But all other answers did not help on my old bash (suse11sp1[3.2.51(1)-release])
The ‘for’ loop refused to expand the indirect array, instead you need to pre-expand it, use that to create another array with your new variable name. My example below shows a double loop, as that is my intended use.
THEBIGLOOP=(New_FOO New_BAR)
FOOthings=(1 2 3)
BARthings=(a b c)
for j in ${THEBIGLOOP[*]}
do
TheNewVariable=$(eval echo ${${j#New_}things[@]})
for i in $TheNewVariable
do
echo $j $i" hello"
echo
done
done
I’m using # to delete the “New_” from the first array entry, then concatenating with “things”, to get “FOOthings”.
${} with echo and eval, then do their thing in order without throwing errors, which is wrapped in a new $() and assigned the new variable name.
$ Test.sh New_FOO 1 hello New_FOO 2 hello New_FOO 3 hello New_BAR a hello New_BAR b hello New_BAR c hello
UPDATE ##### 2018/06/07
I’ve recently discovered one more spin on this issue. The variable created is not actually an array, but a space delimited string.
For the task above this was ok, because of how “for” works, it doesn’t read the array, it is expanded and then looped through, see extract below:
for VARIABLE in 1 2 3 4 5 .. N
do
command1
command2
commandN
done
But, I then needed to use it as an array. For this I needed to perform one more step. I took code verbatim by Dennis Williamson. I’ve tested it and it works fine.
IFS=', ' read -r -a TheNewVariable <<< ${TheNewVariable[@]}
The “IFS=’, ‘” is a variable containing your deliminator. “read” with “-a” cuts and feeds the sting back into the array variable. Note, this has no respect for quotation marks, but there are a few options in read to manage this, e.g. I’ve removed the -r flag which I didn’t need.
So I have now combined this addition in the variable creation, which allows the data to be treated and addressed as it should.
THEBIGLOOP=(New_FOO New_BAR)
FOOthings=(1 2 3)
BARthings=(a b c)
for j in ${THEBIGLOOP[*]}
do
IFS=', ' read -a TheNewVariable <<< $(eval echo ${${j#New_}things[@]})
for i in ${TheNewVariable[@]} #Now have to wrap with {} and expand with @
do
echo $j $i" hello"
echo ${TheNewVariable[$i]} #This would not work in the original code
echo
done
done
Method 4
arrayA=(1 2 3)
arrayB=(a b c)
ARG="$1"
eval echo ${array${ARG}[@]}
dataget (){
eval echo ${array${1}[${2:<a href="https://getridbug.com/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="341974">[email protected]</a>}]}
}
$ dataget A
1 2 3
$ dataget A 0
1
$ dataget B 1
b
note:
escap cotes in case of space!
eval dostuff "${array${1}[${2:<a href="https://getridbug.com/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="e9c4a9">[email protected]</a>}]}"
Method 5
This is how you would create a dynamically named variable (bash version < 4.3).
# Dynamically named array
my_variable_name="dyn_arr_names"
eval $my_variable_name=()
# Adding by index to the array eg. dyn_arr_names[0]="bob"
eval $my_variable_name[0]="bob"
# Adding by pushing onto the array eg. dyn_arr_names+=(robert)
eval $my_variable_name+=(robert)
# Print value stored at index indirect
echo ${!my_variable_name[0]}
# Print value stored at index
eval echo ${$my_variable_name[0]}
# Get item count
eval echo ${#$my_variable_name[@]}
Below is a group of functions that can be used to manage dynamically named arrays (bash version < 4.3).
# Dynamically create an array by name
function arr() {
[[ ! "$1" =~ ^[a-zA-Z_]+[a-zA-Z0-9_]*$ ]] && { echo "Invalid bash variable" 1>&2 ; return 1 ; }
# The following line can be replaced with 'declare -ag $1=()'
# Note: For some reason when using 'declare -ag $1' without the parentheses will make 'declare -p' fail
eval $1=()
}
# Insert incrementing by incrementing index eg. array+=(data)
function arr_insert() {
[[ ! "$1" =~ ^[a-zA-Z_]+[a-zA-Z0-9_]*$ ]] && { echo "Invalid bash variable" 1>&2 ; return 1 ; }
declare -p "$1" > /dev/null 2>&1
[[ $? -eq 1 ]] && { echo "Bash variable [${1}] doesn't exist" 1>&2 ; return 1 ; }
eval $1[$((${#${1}[@]}))]=$2
}
# Update an index by position
function arr_set() {
[[ ! "$1" =~ ^[a-zA-Z_]+[a-zA-Z0-9_]*$ ]] && { echo "Invalid bash variable" 1>&2 ; return 1 ; }
declare -p "$1" > /dev/null 2>&1
[[ $? -eq 1 ]] && { echo "Bash variable [${1}] doesn't exist" 1>&2 ; return 1 ; }
eval ${1}[${2}]=${3}
}
# Get the array content ${array[@]}
function arr_get() {
[[ ! "$1" =~ ^[a-zA-Z_]+[a-zA-Z0-9_]*$ ]] && { echo "Invalid bash variable" 1>&2 ; return 1 ; }
declare -p "$1" > /dev/null 2>&1
[[ $? -eq 1 ]] && { echo "Bash variable [${1}] doesn't exist" 1>&2 ; return 1 ; }
eval echo ${${1}[@]}
}
# Get the value stored at a specific index eg. ${array[0]}
function arr_at() {
[[ ! "$1" =~ ^[a-zA-Z_]+[a-zA-Z0-9_]*$ ]] && { echo "Invalid bash variable" 1>&2 ; return 1 ; }
declare -p "$1" > /dev/null 2>&1
[[ $? -eq 1 ]] && { echo "Bash variable [${1}] doesn't exist" 1>&2 ; return 1 ; }
[[ ! "$2" =~ ^(0|[-]?[1-9]+[0-9]*)$ ]] && { echo "Array index must be a number" 1>&2 ; return 1 ; }
local v=$1
local i=$2
local max=$(eval echo ${#${1}[@]})
# Array has items and index is in range
if [[ $max -gt 0 && $i -ge 0 && $i -lt $max ]]
then
eval echo ${$v[$i]}
fi
}
# Get the value stored at a specific index eg. ${array[0]}
function arr_count() {
[[ ! "$1" =~ ^[a-zA-Z_]+[a-zA-Z0-9_]*$ ]] && { echo "Invalid bash variable " 1>&2 ; return 1 ; }
declare -p "$1" > /dev/null 2>&1
[[ $? -eq 1 ]] && { echo "Bash variable [${1}] doesn't exist" 1>&2 ; return 1 ; }
local v=${1}
eval echo ${#${1}[@]}
}
array_names=(bob jane dick)
for name in "${array_names[@]}"
do
arr dyn_$name
done
echo "Arrays Created"
declare -a | grep "a dyn_"
# Insert three items per array
for name in "${array_names[@]}"
do
echo "Inserting dyn_$name abc"
arr_insert dyn_$name "abc"
echo "Inserting dyn_$name def"
arr_insert dyn_$name "def"
echo "Inserting dyn_$name ghi"
arr_insert dyn_$name "ghi"
done
for name in "${array_names[@]}"
do
echo "Setting dyn_$name[0]=first"
arr_set dyn_$name 0 "first"
echo "Setting dyn_$name[2]=third"
arr_set dyn_$name 2 "third"
done
declare -a | grep "a dyn_"
for name in "${array_names[@]}"
do
arr_get dyn_$name
done
for name in "${array_names[@]}"
do
echo "Dumping dyn_$name by index"
# Print by index
for (( i=0 ; i < $(arr_count dyn_$name) ; i++ ))
do
echo "dyn_$name[$i]: $(arr_at dyn_$name $i)"
done
done
for name in "${array_names[@]}"
do
echo "Dumping dyn_$name"
for n in $(arr_get dyn_$name)
do
echo $n
done
done
Below is a group of functions that can be used to manage dynamically named arrays (bash version >= 4.3).
# Dynamically create an array by name
function arr() {
[[ ! "$1" =~ ^[a-zA-Z_]+[a-zA-Z0-9_]*$ ]] && { echo "Invalid bash variable" 1>&2 ; return 1 ; }
declare -g -a $1=()
}
# Insert incrementing by incrementing index eg. array+=(data)
function arr_insert() {
[[ ! "$1" =~ ^[a-zA-Z_]+[a-zA-Z0-9_]*$ ]] && { echo "Invalid bash variable" 1>&2 ; return 1 ; }
declare -p "$1" > /dev/null 2>&1
[[ $? -eq 1 ]] && { echo "Bash variable [${1}] doesn't exist" 1>&2 ; return 1 ; }
declare -n r=$1
r[${#r[@]}]=$2
}
# Update an index by position
function arr_set() {
[[ ! "$1" =~ ^[a-zA-Z_]+[a-zA-Z0-9_]*$ ]] && { echo "Invalid bash variable" 1>&2 ; return 1 ; }
declare -p "$1" > /dev/null 2>&1
[[ $? -eq 1 ]] && { echo "Bash variable [${1}] doesn't exist" 1>&2 ; return 1 ; }
declare -n r=$1
r[$2]=$3
}
# Get the array content ${array[@]}
function arr_get() {
[[ ! "$1" =~ ^[a-zA-Z_]+[a-zA-Z0-9_]*$ ]] && { echo "Invalid bash variable" 1>&2 ; return 1 ; }
declare -p "$1" > /dev/null 2>&1
[[ $? -eq 1 ]] && { echo "Bash variable [${1}] doesn't exist" 1>&2 ; return 1 ; }
declare -n r=$1
echo ${r[@]}
}
# Get the value stored at a specific index eg. ${array[0]}
function arr_at() {
[[ ! "$1" =~ ^[a-zA-Z_]+[a-zA-Z0-9_]*$ ]] && { echo "Invalid bash variable" 1>&2 ; return 1 ; }
declare -p "$1" > /dev/null 2>&1
[[ $? -eq 1 ]] && { echo "Bash variable [${1}] doesn't exist" 1>&2 ; return 1 ; }
[[ ! "$2" =~ ^(0|[-]?[1-9]+[0-9]*)$ ]] && { echo "Array index must be a number" 1>&2 ; return 1 ; }
declare -n r=$1
local max=${#r[@]}
# Array has items and index is in range
if [[ $max -gt 0 && $i -ge 0 && $i -lt $max ]]
then
echo ${r[$2]}
fi
}
# Get the value stored at a specific index eg. ${array[0]}
function arr_count() {
[[ ! "$1" =~ ^[a-zA-Z_]+[a-zA-Z0-9_]*$ ]] && { echo "Invalid bash variable " 1>&2 ; return 1 ; }
declare -p "$1" > /dev/null 2>&1
[[ $? -eq 1 ]] && { echo "Bash variable [${1}] doesn't exist" 1>&2 ; return 1 ; }
declare -n r=$1
echo ${#r[@]}
}
array_names=(bob jane dick)
for name in "${array_names[@]}"
do
arr dyn_$name
done
echo "Arrays Created"
declare -a | grep "a dyn_"
# Insert three items per array
for name in "${array_names[@]}"
do
echo "Inserting dyn_$name abc"
arr_insert dyn_$name "abc"
echo "Inserting dyn_$name def"
arr_insert dyn_$name "def"
echo "Inserting dyn_$name ghi"
arr_insert dyn_$name "ghi"
done
for name in "${array_names[@]}"
do
echo "Setting dyn_$name[0]=first"
arr_set dyn_$name 0 "first"
echo "Setting dyn_$name[2]=third"
arr_set dyn_$name 2 "third"
done
declare -a | grep 'a dyn_'
for name in "${array_names[@]}"
do
arr_get dyn_$name
done
for name in "${array_names[@]}"
do
echo "Dumping dyn_$name by index"
# Print by index
for (( i=0 ; i < $(arr_count dyn_$name) ; i++ ))
do
echo "dyn_$name[$i]: $(arr_at dyn_$name $i)"
done
done
for name in "${array_names[@]}"
do
echo "Dumping dyn_$name"
for n in $(arr_get dyn_$name)
do
echo $n
done
done
For more details on these examples visit Getting Bashed by Dynamic Arrays by Ludvik Jerabek
Method 6
This worked for me:
I have arrays declared as global from the form:
declare -a total_count
declare -a total_min
declare -a total_max
declare -a total_med
declare -a total_avg
declare -a total_stdev
And I want to to print table that is made of the content of the above arrays without having to do same work over and over.
I want the function to get the prefix of the array name and the number of rows I want to print and do the rest. Main challenge here was to access bash array at specific index with array name made of function variable.
I noticed that use of eval twice can do the work. Without this trick I saw ‘bad substitution’ error.
function print_table {
name=$1
len=$2
# print banner
echo -n "filename "
for col in _count _min _max _med _avg _stdev
do
echo -n "${name}${col} "
done
echo ""
#print data
for ((i=0;i<len;i++)); do
line="count=${${name}_count[$i]}"
eval "$line"
line="min=${${name}_min[$i]}"
eval "$line"
line="max=${${name}_max[$i]}"
eval "$line"
line="med=${${name}_med[$i]}"
eval "$line"
line="avg=${${name}_avg[$i]}"
eval "$line"
line="stdev=${${name}_stdev[$i]}"
eval "$line"
eval "printf "%s %d %f %f %f %f %f n" "${filename[$i]}" "$count" "$min" "$max" "$med" "$avg" "$stdev" "
done
}
Method 7
no way 🙁
if your arrays are that simple, then use associative arrays
declare -A array
array[A]="1 2 3"
array[B]="a b c"
unfortunately, if your arrays are more complicated ( for example array=( "a b" c ) ), that wouldn’t work. Then, you need to think harder about another way to reach your goal.
Method 8
Use eval
arrayA=(1 2 3)
ARG=arrayA
eval echo ${$ARG[@]} # equivalent to eval echo ${arrayA[@]}
# note that we escape the first '$' to prevent from
# its parameter expansion before passing it to echo
All methods was sourced from stackoverflow.com or stackexchange.com, is licensed under cc by-sa 2.5, cc by-sa 3.0 and cc by-sa 4.0