there is an array like this in bash
array_1=(1 2 3 4 5)
array_2=(6 7 8 9 0)
I have another variable that contains 1 or 2 .
array_index=1
So is it possible to create the array name using that variable - like this ?
array_${array_index}[0]
Use variable indirection to read and declare to write:
array_1=(1 2 3 4 5)
array_2=(6 7 8 9 0)
array_index=1
var="array_${array_index}[0]"
echo "The contents of $var is ${!var}"
declare "$var"="Something Else"
echo "Now $var contains ${!var} instead."
This is safer and easier to use correctly than eval.
To create array in bash, exactly to set array's value, you can use eval function, in order to make ${array_index} statement as a real index, so do as follows:
eval array_${array_index}[0]=1
To read value, also eval:
eval echo \${array_${array_index}[0]}
1
Problem is to access a particular element you have to do something like ${array[index]}. But you are also wanting to nest a variable in the array part , which bash won't understand when trying to perform the expansion since it expects array to be a variable.
So the only way I can think of doing this would be to force the array expansion to happen later than your variables. e.g.
> array_1=(1 2 3 4 5)
> array_2=(6 7 8 9 0)
> array_index=1
> eval "echo \${array_$array_index[0]}"
1
As n.m. points out in comments, eval is evil, so you should take care when using it.
Related
I have an array of strings to pass through a script. The script is well-behaved, and will return error code 0 if the string "passes" and non-zero if it "fails." If the string passes, it should be included in a final array to be output or written to file or etc.
The problem I'm having is that the only item ending up in my final array is the first "passing" string.
#!/bin/bash
# validator.sh
if [[ $1 -le 10 ]]; then
exit 0
else
exit 1
fi
#!/bin/bash
# main.sh
numbers=(2 4 6 8 10 12 14 16)
keep=()
for n in ${numbers[#]}; do
if ./validator.sh $n; then
keep+=("$n")
fi
done
echo $keep
Running main.sh produces:
$ ./main.sh
2
but I expect 2 4 6 8 10
Unless you meant keep to be an array of matching elements, change:
keep+=("$n")
to
keep="$keep $n"
That would work with any Bourne compatible shell and is therefore better, if you're looking for BASH specific solution, the below will also work:
keep+="${n} "
If you DO want it to be an array, then in order to output all elements, you can use:
echo ${keep[#]}
As noted by #Jetchisel and #kamilCuk in the comments.
Since you wrote you want to output all elements or save them to a file, I had assumed you don't actually need an array here but perhaps you plan to use this data in other ways later:)
I have following code:
#!/bin/bash
function getSlots {
declare -A slots
for index in `seq 7 10`;
do
slots[$index]=$index
done
echo ${slots[#]}
}
slots=($(getSlots))
for i in ${!slots[#]};
do
echo "$i ${slots[$i]}"
done
When I run, my output is this:
0 10
1 7
2 8
3 9
Why do indexes change when I call the function?
arr=(...) re-indexes the array.
With your current approach, you cannot preserve the indexes as any information about them is lost as soon as you leave the function (you just echo the values).
You can use nameref (requires bash 4.3 or later) to modify the supplied array directly and since you are using only numbers as indexes, regular array will suffice:
#!/usr/bin/env bash
function get_slots {
local index # makes index local
local -n _arr=$1 # _arr points to the supplied array
_arr=() # empties the array
for index in {7..10}; do
_arr[index]=$index # assigns new values to the array
done
}
get_slots slots
for i in "${!slots[#]}"; do
echo "$i ${slots[$i]}"
done
Because the echo ${slots[#]} at the end of the function getSlots expands to echo 10 7 8 9 and that output is what you are assigning to the array slots by doing:
slots=($(getSlots))
Another interesting question would be why echo ${slots[#]} expands to echo 10 7 8 9 and not to echo 7 8 9 10.
That is because the slots inside getSlots is declared as an associative array, not an array, i.e.:
declare -A slots
Replace the line above with:
declare -a slots
and you will get the following output:
0 7
1 8
2 9
3 10
As stated in the LDP: Bash-Beginners-Guide, using [*] and [#] both references all members of an array.
10.2.2. Dereferencing the variables in an array
In order to refer to the content of an item in an array, use curly braces. This is necessary, as you can see from the following example, to bypass the shell interpretation of expansion operators. If the index number is # or *, all members of an array are referenced.
array=(1 2 3)
echo ${array[*]}
# 1 2 3
echo ${array[#]}
# 1 2 3
I'm wondering what's the difference between them and what's the preferable form in terms of portability between shells (Korn, Dash, Bash, Zsh).
I'm building a bash script that gets information from GeekTool and parses the results. The so far the script looks like this:
1 #!/bin/bash
2
3 clear
4
5 geeklets=`osascript <<TEXT
6 Tell Application "GeekTool Helper"
7 geeklets
8 End Tell
9 TEXT`
10
11 IFS="," read -a geekletArray <<< "$geeklets"
12
13 echo "First Element in Array:"
14 echo ${geekletArray[0]}
15
16 echo ""
17
18 echo "All Array Elements:"
19 for element in ${geekletArray[#]}
20 do
21 echo $element
22 done
At line 14 I echo out the first element of the array which returns:
shell geeklet id 49610161-0A3C-49C6-9626-694370DE3101
but on the first iteration of the loop that steps through the array (line 21), the first element array is returned like this:
shell
geeklet
id
49610161-0A3C-49C6-9626-694370DE3101
In the loop the elements are returned in a newline delimited list.
Why is there a difference? Also, if I wanted to grab just the id value of the array (e.g. 49610161-0A3C-49C6-9626-694370DE3101), how would I go about it?
Thanks!
for element in "${geekletArray[#]}"
do
echo "$element"
done
Judicious use of quotes will protect you from undesired word splitting.
Quoting of arrays can be confusing. You can sidestep the issue completely by iterating over the array indices instead.
There are lots of ways to pull out the id value from the resulting string. I am assuming it will always be the last part of a space-delimited string. As such, I am using a bash parameter expansion to effectively remove all occurrences of the pattern *
for index in ${!geekletArray[#]}
do
echo "${geekletArray[$index]}" # entire element
echo "${geekletArray[$index]##* }" # id value only
done
I used the array assignment below in order to simulate a two dimensional array:
for((i=0;i<2;i++))
do
for((j=0;j<3;j++))
do
read TWOD$i[$j]
done
done < hi.txt
The file hi.txt contains these lines:
1
2
3
4
5
6
If I use echo ${TWOD0[2]}, I can print the value 2, but if I am using a variable for the first index, bash throws a syntax error bad substitution:
for((i=0;i<2;i++))
do
printf "%s\n" "${TWOD$i[2]}"
done
Is there any way to extract the elements from the array using a variable for the first index?
You can use indirect expansion:
row="TWOD$i[2]"
printf "%s\n" ${!row}