Reputation: 4008
I want to pass an array to a function and the loop thru it.
is_node ${nodes[@]}
if I try to loop
function is_node(){
for role in "${1[@]}"
do
I get the following error:
bad substitution
If I first try to check the number of arguments, I notice there are more than one.
function is_node(){
if [[ $# -ne 1 ]] then
echo "Error - The number of arguments is not correct. 1 argument(a role name) needed"
I want to pass the array, just as one argument, and pass other arguments after
is_node array status limit
then inside the function loop thru it.
Upvotes: 1
Views: 104
Reputation: 19
Nice Szczerba!
Your solution works perfectly, the contents of the array can change without changing the relative position of other variables, and that sets your answer appart. Here is an example backup script that can handle different sub directory's based on your solution.
#!/bin/bash
#Logging
logpath="/tmp/ram1"
eDate=$( date '+%Y%m%d_%H%M%S' )
ext="log"
#Backup Source/Destination drives and folder
src1="/mymedia"
subs1=(video audio)
lbl1="M_ResQ1"
dest1="/mnt/media/SG_ResQ1/Archive"
src2="/mymedia"
subs2=(TVSeries _In Test pic Theater)
lbl2="M_ResQ2"
dest2="/mnt/media/SG_ResQ2/Archive"
opt="-va --partial --del"
#-q quite
#-n dry run
#-P is = --partial --progress
Arc (){ # $1 subs $2 from $3 lbl $4 dest
declare -a subs=("${!1}")
from="$2"
lbl=$3
dest=$4
if [ -d "$dest" ]; then
for i in "${subs[@]}"; do
#logto=${logpath}/${eDate}_${lbl}_${i}.${ext}
logto=${logpath}/${eDate}_${lbl}.${ext}
echo $logto $lbl $dest
echo -e "\n\nStarting:\n\t${i}\tinto\t${lbl}\n\t${eDate}\n\t${opt}\n\n" | tee -a ${logto}
rsync ${opt} ${from}/${i} ${dest}/ | tee -a ${logto}
done
echo $( date '+Done %Y%m%d_%H%M%S' ) | tee -a ${logto}
cp ${logto} ${dest}/
else
echo -e "Not mounted or wrong drive"
fi
}
Arc "subs1[@]" $src1 $lbl1 $dest1
Arc "subs2[@]" $src2 $lbl2 $dest2
Upvotes: 0
Reputation: 85550
The question is perfectly valid and don't think its a duplicate of Passing arrays as parameters in bash.
The problem with passing the array as argument to the function as "${nodes[@]}"
or ${nodes[@]}
in this case would be at the receiving side, the array contents are not kept intact, because the contents of the array is expanded before the function is called. So when the arguments are unpacked at the receiver, they are split at $1
, $2
till the size of the array. You could see it from this simple example,
set -x
newf() { echo "$1"; echo "$2"; echo "$3"; }
arr=(1 2 3)
newf "${arr[@]}"
+ newf 1 2 3
+ echo 1
1
+ echo 2
2
+ echo 3
3
as you can see the array arr
is expanded to the list of positional arguments while the intention was to use an array.
So given this problem and with your claim that you have additional argument flags after the array, you need to identify in the receiver side, how to start processing arguments after the array. The best way would be to pass the array expansion using *
, so that the elements quoted as a whole.
So assuming your function expects 3 arguments to it, you can define it as below. The read
command on the receiver will split the whole string of array content to individual elements and store it in the array arrayArgs
and you can parse it as you wish.
is_node(){
(( $# < 3 )) && { printf 'insufficient args provided' >&2; return 1; }
read -ra arrayArgs <<<"$1"
printf 'Printing the array content \n'
for element in "${arrayArgs[@]}"; do
printf '%s\n' "$element"
done
printf '2nd arg=%s 3rd arg=%s\n' "$2" "$3"
}
and pass the array as
list=(1 2 3)
is_node "${list[*]}" 4 5
Upvotes: 1
Reputation: 189317
You can pass in a list of arguments any way you like. The arguments to the function are simply "$@"
.
is_node(){
for role in "$@"; do
: something with "$role"
done
}
is_node "${nodes[@]}"
Notice also the proper use of quoting, and the omission of the (gratuitous, here) Bash-only keyword function
.
More tangentially, the shell assumes in "$@"
if you don't pass an explicit list of tokens, so this can (slightly obscurely) be simplified to for role; do
If you have a fixed number of other arguments, just put them before the variable-length list of arguments.
Upvotes: 0
Reputation: 273
I assume that you want to write function with both arguments - array and traditional "single" ones. If I am mistaken please let me know.
My solution:
#!/bin/bash
function_with_array_and_single_argument () {
declare -a _array1=("${!1}")
echo "${_array1[@]}"
echo $2
}
array="a
b
c"
function_with_array_and_single_argument "array[@]" "Szczerba"
Output:
$ ./script.sh
a
b
c
Szczerba
Upvotes: 1