Struggling for a while passing an array as argument but it's not working anyway. I've tried like below:
#! /bin/bash
function copyFiles{ arr="$1" for i in "${arr[@]}"; do echo "$i" done
}
array=("one" "two" "three")
copyFiles $arrayAn answer with explanation would be nice.
Edit: Basically, i will eventually call the function from another script file. Plz explain the constraints if possible.
110 Answers
Expanding an array without an index only gives the first element, use
copyFiles "${array[@]}"instead of
copyFiles $arrayUse a she-bang
#!/bin/bashUse the correct function syntax
Valid variants are
function copyFiles {…} function copyFiles(){…} function copyFiles() {…}instead of
function copyFiles{…}Use the right syntax to get the array parameter
arr=("$@")instead of
arr="$1"
Therefore
#!/bin/bash
function copyFiles() { arr=("$@") for i in "${arr[@]}"; do echo "$i" done
}
array=("one 1" "two 2" "three 3")
copyFiles "${array[@]}"Output is (my script has the name foo)
$ ./foo
one 1
two 2
three 3 6 If you want to pass one or more arguments AND an array, I propose this change to the script of @A.B.
Array should be the last argument and only one array can be passed
#!/bin/bash
function copyFiles() { local msg="$1" # Save first argument in a variable shift # Shift all arguments to the left (original $1 gets lost) local arr=("$@") # Rebuild the array with rest of arguments for i in "${arr[@]}"; do echo "$msg $i" done
}
array=("one" "two" "three")
copyFiles "Copying" "${array[@]}"Output:
$ ./foo
Copying one
Copying two
Copying three 7 You could also pass the array as a reference. i.e.:
#!/bin/bash
function copyFiles { local -n arr=$1 for i in "${arr[@]}" do echo "$i" done
}
array=("one" "two" "three")
copyFiles arraybut note that any modifications to arr will be made to array.
7There are couple of problems. Here is the working form :
#!/bin/bash
function copyFiles { arr=( "$@" ) for i in "${arr[@]}"; do echo "$i" done
}
array=("one" "two" "three")
copyFiles "${array[@]}"There need to be at least a space between function declaration and
{You can not use
$array, asarrayis an array not a variable. If you want to get all the values of an array use"${array[@]}"In you main function declaration you need
arr="$@"as"${array[@]}"will expand to the indexed values separated by spaces, if you use$1you would get only the first value. To get all the values usearr="${arr[@]}".
Here follows a slightly larger example. For explanation, see the comments in the code.
#!/bin/bash -u
# ==============================================================================
# Description
# -----------
# Show the content of an array by displaying each element separated by a
# vertical bar (|).
#
# Arg Description
# --- -----------
# 1 The array
# ==============================================================================
show_array()
{ declare -a arr=("${@}") declare -i len=${#arr[@]} # Show passed array for ((n = 0; n < len; n++)) do echo -en "|${arr[$n]}" done echo "|"
}
# ==============================================================================
# Description
# -----------
# This function takes two arrays as arguments together with their sizes and a
# name of an array which should be created and returned from this function.
#
# Arg Description
# --- -----------
# 1 Length of first array
# 2 First array
# 3 Length of second array
# 4 Second array
# 5 Name of returned array
# ==============================================================================
array_demo()
{ declare -a argv=("${@}") # All arguments in one big array declare -i len_1=${argv[0]} # Length of first array passad declare -a arr_1=("${argv[@]:1:$len_1}") # First array declare -i len_2=${argv[(len_1 + 1)]} # Length of second array passad declare -a arr_2=("${argv[@]:(len_1 + 2):$len_2}") # Second array declare -i totlen=${#argv[@]} # Length of argv array (len_1+len_2+2) declare __ret_array_name=${argv[(totlen - 1)]} # Name of array to be returned # Show passed arrays echo -en "Array 1: "; show_array "${arr_1[@]}" echo -en "Array 2: "; show_array "${arr_2[@]}" # Create array to be returned with given name (by concatenating passed arrays in opposite order) eval ${__ret_array_name}='("${arr_2[@]}" "${arr_1[@]}")'
}
########################
##### Demo program #####
########################
declare -a array_1=(Only 1 word @ the time) # 6 elements
declare -a array_2=("Space separated words," sometimes using "string paretheses") # 4 elements
declare -a my_out # Will contain output from array_demo()
# A: Length of array_1
# B: First array, not necessary with string parentheses here
# C: Length of array_2
# D: Second array, necessary with string parentheses here
# E: Name of array that should be returned from function.
# A B C D E
array_demo ${#array_1[@]} ${array_1[@]} ${#array_2[@]} "${array_2[@]}" my_out
# Show that array_demo really returned specified array in my_out:
echo -en "Returns: "; show_array "${my_out[@]}" The best way is to pass as position arguments. Nothing else. You may pass as string, but this way may cause some troubles. Example:
array=(one two three four five)
function show_passed_array(){ echo $@
}or
function show_passed_array(){ while $# -gt 0;do echo $1;shift done
} show_passed_array ${array[@]}output:
one two three four fiveYou mean if array value has space symbols you must quote elements first before pass for accessing value by index in function use $1 $2 $3 ... position parameters. Where index 0 -> 1, 1 -> 2,... To iterate access it is best to use always $1 and after Shift. Nothing additional is needed. You may pass arguments without any array like this:
show_passed_array one two three four fivebash media automatically builds an array from passed arguments that passed them to function and then you have position arguments. Furthermore when you write ${array[2]} you really write consequent argument one two three four and passed them to the function. So those calls are equivalent.
As ugly as it is, here is a workaround that works as long as you aren't passing an array explicitly, but a variable corresponding to an array:
function passarray()
{ eval array_internally=("$(echo '${'$1'[@]}')") # access array now via array_internally echo "${array_internally[@]}" #...
}
array=(0 1 2 3 4 5)
passarray array # echo's (0 1 2 3 4 5) as expectedI'm sure someone can come up with a cleaner implementation of the idea, but I've found this to be a better solution than passing an array as "{array[@]"} and then accessing it internally using array_inside=("$@"). This becomes complicated when there are other positional/getopts parameters. In these cases, I've had to first determine and then remove the parameters not associated with the array using some combination of shift and array element removal.
A purist perspective likely views this approach as a violation of the language, but pragmatically speaking, this approach has saved me a whole lot of grief. On a related topic, I also use eval to assign an internally constructed array to a variable named according to a parameter target_varname I pass to the function:
eval $target_varname=$"(${array_inside[@]})" 4 I propose to avoid any limitation about "passing array(s) args" to a function by... passing strings, and make them array INSIDE the function :
#!/bin/bash
false_array1='1 2 3 4 5'
false_array2='my super fake array to deceive my function'
my_normal_string='John'
function i_love_arrays(){ local myNumbers=("$1") local mySentence=("$2") local myName="$3" echo "My favorite numbers are, for sure: " for number in ${myNumbers[@]} do echo $number done echo "Let's make an ugly split of a sentence: " for word in ${mySentence[@]} do echo $word done echo "Yeah, I know, glad to meet you too. I'm ${myName}."
}
i_love_arrays "${false_array1}" "${false_array2}" "${my_normal_string}" Using references (easiest way; best and only way for passing associative arrays):
print()
{ [ "$1" = "arr" ] || { declare -n arr; arr="$1"; } # The name of reference mustn't match the input coming to the function. # If it does, use the name directly as array instead of reference. echo "${arr[@]}"
}
print arrFor normal (non-associative) arrays:
Here is a function that copies the passed array in new (local) array for usage. Can be time-taking for long arrays.
copy_fn()
{ eval local newarr=\(\${$1[@]}\) echo ${newarr[@]}
}Further, like in C++ you can only pass an array pointer, but in python, as a shared variable which you can modify as well.
Here is the shared variables approach.. useful for long arrays.
modify_arr()
{ eval local ptr=$1 # works as if ptr is a pointer to actual "array-name" (arr) # the original array is, thus, still same, not a copy eval $ptr+=(1) # modify the array echo ${ptr[@]} # will print name of the array (arr) eval echo \$\{$ptr[@]\} # will print the actual array (arr)
}
modify_arr a
echo ${a[@]}-Himanshu
2@SBF
Your idea sparked some thinking on a situation I needed where two arrays were to be compared.
You can pass multiple arrays using your technique with a minor modification. See below
Thank you!
(works in bash 3.3 on Debian)
#!/bin/bash
#
function TakeTwoArrays() { local Var1="$1" local Var2="$2" local NumArray1="$3" local NumArray2="$4" shift 4 local -a AllArray=("$@") local -a Array1="${AllArray[@]:0:${NumArray1}}" local -a Array2="${AllArray[@]:${NumArray1}}" echo "Var1 is ${Var1}" echo "Var2 is ${Var2}" echo "Array1 has ${NumArray1} entries:" printf '%s\n' "${Array1[@]}" echo "Array2 has ${NumArray2} entries:" printf '%s\n' "${Array2[@]}"
}
FirstArg="So Far"
SecondArg="So Good"
Array1Arg=("But" "the" "real" "question" "is")
NumArray1Arg="${#Array1Arg[@]}"
Array2Arg=("What" "happens" "with" "the" "second" "set" "of" "elements")
NumArray2Arg="${#Array2Arg[@]}"
TakeTwoArrays "${FirstArg}" "${SecondArg}" "${NumArray1Arg}" "${NumArray2Arg}" "${Array1Arg[@]}" "${Array2Arg[@]}"