Create array from many variables - arrays

Using bash I am trying to create an echo command with a list of variables. The number of variables can differ on what I am trying to do but I know how many I have as I count them in $ctr1. An example of what it should look like is:
echo "$var1,$var2,$var3"
etc. with the last variable the same number as the counter.
Can someone give me an idea as to what I should be doing, an example would be great. I know it could be done with if statements with a line for the possible number in the counter but that is not practical as there can be from 1 to 50+ variables in a line. I do not know if this should be an array or such like nor how to put this together. Any assistance on this would be a help.

Yes, this should be an array instead.
Instead of doing e.g.
var1=foo
var2=bar
var3=quux
ctr1=3
echo "${var1},${var2},${var3}"
you could do
var=("foo" "bar" "quux")
( IFS=,; echo "${var[*]}" )
Example:
$ cat test.sh
#!/bin/bash
# the following is equivalent to doing
# ( IFS=,; echo "$*" )
# but that wouldn't be a good example, would it?
for argument in "$#"; do
var+=( "${argument}" )
done
( IFS=,; echo "${var[*]}" )
.
$ ./test.sh foo
foo
$ ./test.sh foo bar
foo,bar
$ ./test.sh foo bar "qu ux"
foo,bar,qu ux

Related

Bash shift changes expected value of associative array

I made a simple script, that matches the positional arguments starting with dash, and saves them into an associative array.
declare -A opts
for i; do
[[ "$i" =~ - ]] && opts[$i]=1
done
shift "${#opts[*]}"
echo "opts: ${opts[*]}"
echo "!opts: ${!opts[-d]}"
echo "Query: $*"
For the call ./script -d hello world the output is:
opts: 1
!opts: hello
Query: hello world
Which is unexpected, since the key of ${!opts[-d]} is supposed to be -d itself if defined. This behavior happens because of the shift command, when it is removed from the code, the output is as expected:
opts: 1
!opts: -d
Query: -d hello world
Why does shift interfere with the created associative array?
The associative array isn't being changed; you can verify this by putting a declare -p opts after the shift, which will give
declare -A opts=([-d]="1" )
The problem: ${!opts[-d]} doesn't do what you seem to expect. First, opts[-d] is looked up, which is the value 1. Then the ! means that's used as the name of a variable to substitute - so it's effectively the same as $1, which, after the shift, is hello.
If you want to print out all the keys of the associative array, use ${!opts[#]}. The leading ! has two different meanings in bash paramater expansion depending on if used with an array with # or * in the brackets, or an index/normal variables.

Bash - Print value of variable in array

I want to do something like this
A='123'
B='143'
C='999'
declare -a arr=(A B C)
for i in "{$arr[#]}"
do
echo "#i" "$i"
done
Which should give me the output of
A 123
B 143
C 999
But instead I receive the variable names, not the value in the output (I just see "A #i" in the output...
If you want to store the variable names in the loop, rather than copy their values, then you can use the following:
for i in "${arr[#]}"; do
echo "${!i}"
done
This means that the value of i is taken as a name of a variable, so you end up echoing $A, $B and $C in the loop.
Of course, this means that you can print the variable name at the same time, e.g. by using:
echo "$i: ${!i}"
It's not exactly the same, but you may also be interested in using an associative array:
declare -A assoc_arr=( [A]='123' [B]='143' [C]='999' )
for key in "${!assoc_arr[#]}"; do
echo "$key: ${assoc_arr[$key]}"
done
I suggest to add $:
declare -a arr=("$A" "$B" "$C")

bad array subscript error in associative array

I am trying to create a dictionary program in Bash with the following options : 1. Add a word
2. Update meaning
3. Print dictionary
4. Search a word
5. Search by keyword
For the same, I am creating 2 associative arrays, 1 to store the word - meaning and other to store words-keyword.
The problem is I am not able to store values in the array. Everytime I try to do it, it gives me an error
dict[$word]: bad array subscript
Here is the code for part 1
echo
echo -n "Enter a word : "
read $word
echo
echo -n "Enter it's meaning : "
read $meaning
echo
echo -n "Enter some keywords(with space in between) to describe the word : "
read $keyword
dict[$word]=$meaning
keywords[$word]=$keyword
;;
I also tried inserting the following code to remove new line as suggested in some posts but ended up with same result.
word=`echo $word | grep -s '\n'`
keyword=`echo $keyword | grep -s '\n'`
Have also tried the following way :
dict["$word"]="$meaning"
keywords["$word"]="$keyword"
;;
Output :
dict[$word]: bad array subscript
When reading a variable you preface the variable name with a $ (or wrap in $( and )).
When assigning a value to a variable you do not preface the variable name with a $.
In your example your 3x echo/read sessions are attempting to assign values to your variables, but you've prefaced your variables with $, which means your variables are not getting set as you expect; this in turn could be generating your error because $word is not set/defined (depends on version of bash).
You should be able to see what I mean with the following code snippet:
unset word
echo
echo -n "Enter a word : "
read $word
echo ".${word}."
What do you get as ouput? .. ? .<whatever_you_typed_in>. ?
You may also have a problem with your associative arrays (depending on bash version); as George has mentioned, you should play it safe and explicitly declare your associative arrays.
I would suggest editing your input script like such (remove leading $ on your read variables; explicitly declaring your associative arrays):
echo
echo -n "Enter a word : "
read word
echo
echo -n "Enter it's meaning : "
read meaning
echo
echo -n "Enter some keywords(with space in between) to describe the word : "
read keyword
# print some debug messages:
echo "word=.${word}."
echo "meaning=.${meaning}."
echo "keyword=.${keyword}."
# declare arrays as associative
declare -A dict keywords
# assign values to arrays
dict[$word]=$meaning
keywords[$word]=$keyword
# verify array indexes and values
echo "dict index(es) : ${!dict[#]}"
echo "dict value(s) : ${dict[#]}"
echo "keywords index(es): ${!keywords[#]}"
echo "keywords value(s) : ${keywords[#]}"
In my bash 4.4 , this is not raising any error but is not working correctly either:
$ w="one";m="two";d["$w"]="$m";declare -p d
declare -a d=([0]="two")
It is obvious that bash determines array d as a normal array and not as an associative array.
On the contrary, this one works fine:
$ w="one";m="two";declare -A d;d["$w"]="$m";declare -p d
declare -A d=([one]="two" )
According to bash manual, you need first to declare -A an array in order to be used as an associative one.

Bash arrays: compound assignments fail

I have no idea why the compound array initialization does not work for me.
minimal example:
#!/bin/bash
#
MINRADIUS=( 'foo' 'bar' 'foobar' )
for i in {0..2..1}; do echo ${MINRADIUS[$i]}; done
output is
$ sh test.sh
(foo bar foobar)
with 2 additional blank lines.
Fieldwise initialization works:
#!/bin/bash
#
MINRADIUS[0]="foo"
MINRADIUS[1]="bar"
MINRADIUS[2]="foobar"
for i in {0..2..1}; do echo ${MINRADIUS[$i]}; done
$ sh test.sh
foo
bar
foobar
I have tried every possible combination of braces, quotes and "declare -a".
Could it be related to my bash version? I'm running version 4.1.2(1).
The problem is, you are not using bash. Shebang doesn't matter if you run your script throught sh. Try bash instead.
I tried the below code and its working fine for me. Using bash 3.2.39(1)-release
#!/bin/bash
#
MINRADIUS=( 'foo' 'bar' 'foobar' )
for i in {0,1,2}; do echo ${MINRADIUS[$i]}; done
Output for this was
foo
bar
foobar
For me with your code it was giving an error
line 4: {0..1..2}: syntax error: operand expected (error token is "{0..1..2}")
I would suspect there is some quoting going on in your first example using compound assignments. Using this modified test script:
#!/bin/bash
echo "SHELL=${SHELL}"
echo 'Single-quoted v:'
v='(a b c)'; for i in {0..2}; do echo "v[$i]=${v[i]}"; done
echo 'Double-quoted v:'
v="(a b c)"; for i in {0..2}; do echo "v[$i]=${v[i]}"; done
echo 'Unquoted v:'
v=(a b c); for i in {0..2}; do echo "v[$i]=${v[i]}"; done
I get the following output:
$ sh test.sh
SHELL=/bin/bash
Single-quoted v:
v[0]=(a b c)
v[1]=
v[2]=
Double-quoted v:
v[0]=(a b c)
v[1]=
v[2]=
Unquoted v:
v[0]=a
v[1]=b
v[2]=c
If you quote the assignment, it becomes a simple variable assignment; a simple mistake to make that is easily overlooked.

Returning array from a Bash function

I am making a bash script and I have encountered a problem. So let's say I got this
function create_some_array(){
for i in 0 1 2 3 .. 10
do
a[i]=$i
done
}
create_some_array
echo ${a[*]}
Is there any way I can make this work? I have searched quite a lot and nothing I found worked.
I think making the a[] a global variable should work but I can't find something that actually works in my code. Is there any way to return the array from the function to main program?
Thanks in advance
This won't work as expected when there are whitespaces in the arrays:
function create_some_array() {
local -a a=()
for i in $(seq $1 $2); do
a[i]="$i $[$i*$i]"
done
echo ${a[#]}
}
and worse: if you try to get array indices from the outside "a", it turns out to be a scalar:
echo ${!a[#]}
even assignment as an array wont help, as possible quoting is naturally removed by the echo line and evaluation order cannot be manipulated to escape quoting: try
function create_some_array() {
...
echo "${a[#]}"
}
a=($(create_some_array 0 10))
echo ${!a[#]}
Still, printf seems not to help either:
function create_some_array() {
...
printf " \"%s\"" "${a[#]}"
}
seems to produce correct output on one hand:
$ create_some_array 0 3; echo
"0 0" "1 1" "2 4" "3 9"
but assignment doesn't work on the other:
$ b=($(create_some_array 0 3))
$ echo ${!b[#]}
0 1 2 3 4 5 6 7
So my last trick was to do assignment as follows:
$ eval b=("$(create_some_array 0 3)")
$ echo -e "${!b[#]}\n${b[3]}"
0 1 2 3
3 9
Tataaa!
P.S.: printf "%q " "${a[#]}" also works fine...
This works fine as described. The most likely reason it doesn't work in your actual code is because you happen to run it in a subshell:
cat textfile | create_some_array
echo ${a[*]}
would not work, because each element in a pipeline runs in a subshell, and
myvalue=$(create_some_array)
echo ${a[*]}
would not work, since command expansion happens in a subshell.
You can make an array local to a function, and then return it:
function create_some_array(){
local -a a=()
for i in $(seq $1 $2); do
a[i]=$i
done
echo ${a[#]}
}
declare -a a=()
a=$(create_some_array 0 10)
for i in ${a[#]}; do
echo "i = " $i
done
Hi here is my solution:
show(){
local array=()
array+=("hi")
array+=("everything")
array+=("well?")
echo "${array[#]}"
}
for e in $(show);do
echo $e
done
Try this code on: https://www.tutorialspoint.com/execute_bash_online.php
Both these work for me with sh and bash:
arr1=("192.168.3.4" "192.168.3.4" "192.168.3.3")
strArr=$(removeDupes arr1) # strArr is a string
for item in $strArr; do arr2+=("$item"); done # convert it to an array
len2=${#arr2[#]} # get array length
echo "${len2}" # echo length
eval arr3=("$(removeDupes arr1)") # shellcheck does not like this line and won't suppress it but it works
len3=${#arr3[#]} # get array length
echo "${len3}" # echo length
As an aside, the removeDupes function looks like this:
removeDupes() {
arg="$1[#]"
arr=("${!arg}")
len=${#arr[#]}
resultArr=()
# some array manipulation here
echo "${resultArr[#]}"
}
This answer is based on but better explains and simplifies the answers from #Hans and #didierc

Resources