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 $array An 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 6If 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 7You 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 array but 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 five You 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 five bash 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 expected I'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[@]})" 4I 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 arr For 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[@]}"