set values to elements of an array in a function - bash

In a bash script, I would like to put the following code that assigns values to each element of several arrays into a function
for (( i=0 ; i < ${#themes[#]} ; i+=1 )); do
c_bit_mins[i]=-5
c_bit_maxs[i]=15
gamma_bit_mins[i]=-15
gamma_bit_maxs[i]=3
done
i.e. something like
function set_values()
{
for (( i=0 ; i < ${#themes[#]} ; i+=1 )); do
c_bit_mins[i]=-5
c_bit_maxs[i]=15
gamma_bit_mins[i]=-15
gamma_bit_maxs[i]=3
done
}
How to do it? Especially when these arrays are not seen as global inside the function.
Thanks and regards!

You can make a variable local by using the local command:
local c_bit_mins c_bit_maxs gamma_bit_mins gamma_bit_maxs
However, you can't "return" an array out of a shell function. The return value of a shell function is always an integer. Non-integer values are typically "returned" by echoing them and reading them back in using $(...) in the surrounding program. But that will be completely weird to do with arrays and four of them.

The arrays are global unless you declare them to be local.
$ unset a
$ test() { echo ${a[3]}; a[4]=456; }
$ a[3]=123
$ test
123
$ echo ${a[4]}
456
$ echo ${a[3]}
123

Related

zsh: return associative array from function

How to return assciative arrays from zsh function?
I tried:
creatAARR() {
declare -A AARR=([k1]=2 [k2]=4)
return $AARR
}
creatAARR
But i get error:
creatAARR:return:2: too many arguments
What is the right way?
EDIT:
I captured output to standard output, like how #chepner suggests, but the new variable doesn't seem to behave like an associative array:
creatAARR() {
declare -A AARR=([k1]=2 [k2]=4)
echo "$AARR"
}
declare -A VALL
NEW_ARR=$(creatAARR)
echo "$NEW_ARR" # 2 4
echo "k1: $NEW_ARR[k1]" # prints just k1:
return
Any suggestions?
return accepts only an integer and sets the exit status of the function.
Shell commands cannot actually return values. If you want pass information to the caller of your function, you have a couple of options available to you:
You could print your return value, but this then relies on you to properly format your output and for the caller to correctly parse it. For associative arrays, there are so many ways that this can go wrong; I wouldn’t recommend doing this.
In Zsh, there is a convention that, to communicate a return value, a function can set $REPLY to a scalar value or $reply to an array. Unfortunately, there is no convention for passing associative arrays. You could, of course, put your key-value pairs simply as elements in the non-associative array $reply and then let the caller cast it to or wrap it in an associative array, but this would break the convention and thus might violate your caller's expectations.
The, in my opinion, best approach is to let the caller specify the name of an associative array, which you can then populate with values. This is also handy when you want to return multiple values of any type, since you can let the caller specify multiple variable names.
This last approach you can use as follows:
% creatAARR() {
# Restrict $name to function scope.
local name=$1
# Delete $1, so $# becomes the other args.
shift
# Assign elements to array.
set -A "$name" "$#"
}
% typeset -A AARR=() # Declare assoc. array
% creatAARR AARR k1 2 k2 4
% typeset -p1 AARR # Print details
typeset -A AARR=(
[k1]=2
[k2]=4
)
I've been able to get my requirement with this:
declare -A VAR1
declare -A VAR2
TYPE_VAR1="TYPE_VAR1"
TYPE_VAR2="TYPE_VAR2"
creatAARR() {
declare -A AARR=([k1]=2 [k2]=4)
case $1 in
"$TYPE_VAR1")
set -A VAR1 ${(kv)AARR}
;;
"$TYPE_VAR2")
set -A VAR2 ${(kv)AARR}
;;
esac
}
creatAARR $TYPE_VAR1 $VAR1
echo "${(kv)VAR1}"
return
There may be a better way, but this is what works for me now.
Please feel free to add in your methods.

How can I create an array whose name includes a variable?

I'm having some trouble writing a command that includes a String of a variable in Bash and wanted to know the correct way to do it.
I want to try and fill the Row arrays with the numbers 1-9 but I'm getting myself stuck when trying to pass a variable Row$Line[$i]=$i.
Row0=()
Row1=()
Row2=()
FillArrays() {
for Line in $(seq 0 2)
do
for i in $(seq 1 9)
do
Row$Line[$i]=$i
done
done
}
I can get the desired result if I echo the command but I assume that is just because it is a String.
I want the for loop to select each row and add the numbers 1-9 in each array.
FillArrays() {
for ((Line=0; Line<8; Line++)); do
declare -g -a "Row$Line" # Ensure that RowN exists as an array
declare -n currRow="Row$Line" # make currRow an alias for that array
for ((i=0; i<9; i++)); do # perform our inner loop...
currRow+=( "$i" ) # ...and populate the target array...
done
unset -n currRow # then clear the alias so it can be reassigned later.
done
}
References:
https://wiki.bash-hackers.org/syntax/ccmd/c_for describes the C-style for loop in bash
BashFAQ #6 discusses indirect reference and assignment in detail, including techniques that precede namevars.
Variable expansion happens too late for an assignment to understand it. You can delay the assignment by using the declare builtin. -g is needed in a function to make the variable global.
Also, you probably don't want to use $Line as the array index, but $i, otherwise you wouldn't populate each line array with numbers 1..9.
#! /bin/bash
Row0=()
Row1=()
Row2=()
FillArrays() {
for Line in $(seq 0 8)
do
for i in $(seq 1 9)
do
declare -g Row$Line[$i]=$i
done
done
}
FillArrays
echo "${Row1[#]}"
But note that using variables as parts of variable names is dangerous. For me, needing this always means I need to switch from the shell to a real programming language.

copy the value of the variable rather than reference bash script

I am a bit new to the bash scripting. So please bear with me. I am trying to create a table and assign the values in for loop like this:
packages=("foo" "bar" "foobar")
packageMap=()
function test() {
i=0;
for package in "${packages[#]}"
do
echo $i
packageMap[$package]=$i
i=$(expr $i + 1)
done
}
test
echo the first value is ${packageMap["foo"]}
The output for this is:
0
1
2
the first value is 2
While my expected output is:
0
1
2
the first value is 0
So basically the variable's reference is being assigned to this rather than the value. How to solve this?
My bash version:
GNU bash, version 3.2.57(1)-release (x86_64-apple-darwin16)
TIA
bash 3.2 only has indexed arrays, so packageMap[$package] only works as intended if $package is an integer, not an arbitrary string.
(What you are observing is $package being evaluated in an arithmetic context, where foo, bar, and foobar are recursively expanded until you get an integer value. Undefined variables expand to 0, so packageMap[foo] is equivalent to packageMap[0].)
If you were using bash 4 or later, you could use an associative array:
packages=("foo" "bar" "foobar")
declare -A packageMap
test () {
i=0
for package in "${packages[#]}"
do
echo $i
packageMap[$package]=$i
i=$(($i + 1))
done
}
Given that i is the same as the index of each element of packages, you could also write
test () {
for i in "${!packages[#]}"; do
package=${packages[i]}
packageMap[$package]=$i
done
}
instead of explicitly incrementing i.
As chep says, there are no associative arrays in bash 3. That said, if you don't mind wasting a bit of CPU, you can use functions to similar effect:
#!/bin/bash
packages=("foo" "bar" "foobar")
function packagemap () {
local i
for i in "${!packages[#]}"; do
[[ ${packages[$i]} = $1 ]] && echo "$i" && return
done
echo "unknown"
}
echo "the first value is $(packagemap "foo")"
The ${!array[#]} construct expands to the set of indices for the array, which for a normally non-associative array consist of incrementing integers starting at 0. But array members can be removed without the indices being renumbered (i.e. unset packages[1]), so it's important to be able to refer to actual indices rather than assuming they're sequential with for loop that simply counts.
And I note that you're using Darwin. Remember that you really need it, you can install bash 4 using Homebrew or MacPorts.

Echo $variable$counter in a "for" loop BASH

n=1
test=1000
test1=aaa
I'm trying:
echo $test$n
to get
aaa
But I get
10001
I'm trying to use it that way because I have variables: lignePortTCP1,lignePortTCP2,lignePortTCP1, ETC in a for loop like this:
declare -i cpt3
cpt3=0
for ((i = 1; i <= cpt; i++)); do
cpt3=cpt3+1
echo "Port/Protocole : $lignePortTCP$cpt3 - Nom du Service : $ligneServiceTCP$cpt3"
done
Given the assigned variables
n=1
test1=aaa
...and you want to print aaa given the values of test and n, then put the name you want to expand in its own variable, and expand that with the ! operator, like so:
varname="test$n"
echo "${!varname}"
This is explicitly discussed in BashFAQ #6.
That said, variable indirection is not a particularly good practice -- usually, you can do better using arrays, whether associative or otherwise.
For instance:
test=( aaa bbb ccc )
n=0
echo "${test[n]}"
...for values not starting at 0:
test=( [1]=aaa [2]=bbb [3]=ccc )
n=1
echo "${test[n]}"
If you want to subtract the values of test and n, wrap the computation in $(( ... )) and use
the - operator:
$((test-n))
You could also use eval, though it's probably not better than the technique provided by Charles Duffy.
$ n=1
$ test=1000
$ test1=aaa
$ eval echo \${test$n}
aaa
One way would be to use ${!}, but you have to store the combined name in its own variable for that to work:
var=test$n
echo "${!var}"
If you have control over how the variables get assigned in the first place, it would be better to use an array. Instead of lignePortTCP1, lignePortTCP2, etc., you would assign the values to lignePortTCP[0], lignePortTCP[1], etc. and then retrieve them with ${lignePort[$n]}.

how to use array as function variable in bash

I want to pass an array parameter to a function in bash, and writing some testing code as:
#!/bin/sh
function foo {
a=$1;
for i in ${a[#]} ; do
echo $i
done
}
names=(jim jerry jeff)
foo ${names[#]}
the above code just show jim, rather than the three j*. so my question is:
why my code doesn't work
what's the right way to do it
#!/bin/bash
function foo {
a=($*)
for i in ${a[#]}
do
echo $i
done
}
names=(jim jerry jeff)
foo ${names[#]}
Your code did not show jim to me, but "names", literally. You have to pass the whole array. And you have to recapture it with a=$($).
The manpage part in bash about Arrays is rather long. I only cite one sentence:
Referencing an array variable without a subscript is equivalent to referencing the array with a subscript of 0.
You're fairly close; the biggest problem was the command a=$1, which assigns only the first parameter ($1) to a, while you want to assign the entire list of parameters ($#), and assign it as an array rather than as a string. Other things I corrected: you should use double-quotes around variables whenever you use them to avoid confusion with special characters (e.g. spaces); and start the script with #!/bin/bash, since arrays are a bash extension, not always available in a brand-X shell.
#!/bin/bash
function foo {
a=("$#")
for i in "${a[#]}" ; do
echo "$i"
done
}
names=(jim jerry jeff "jim bob")
foo "${names[#]}"
For example like this:
my_array[0]="jim"
my_array[1]="jerry"
function foo
{
#get the size of the array
n=${#my_array[*]}
for (( Idx = 0; Idx < $n; ++Idx )); do
echo "${my_array[$Idx]}"
done
}

Resources