Как передать массив в качестве аргумента функции?

Некоторое время пытаюсь передать массив в качестве аргумента, но он все равно не работает. Я пробовал, как показано ниже:

#! /bin/bashfunction copyFiles{   arr="$1"   for i in "${arr[@]}";      do          echo "$i"      done}array=("one" "two" "three")copyFiles $array

Было бы неплохо получить ответ с объяснением.

Редактировать: В принципе, я в конечном итоге вызову функцию из другого файла скрипта. Пожалуйста, объясните ограничения, если это возможно.

  • Расширение массива без индекса дает только первый элемент, используйте

    copyFiles "${array[@]}"

    вместо

    copyFiles $array
  • Используй she-bang

    #!/bin/bash
  • Используйте правильный синтаксис функции

    Допустимыми вариантами являются

    function copyFiles {…}function copyFiles(){…}function copyFiles() {…}

    вместо

    function copyFiles{…}
  • Используйте правильный синтаксис для получения параметра массива

    arr=("$@")

    вместо

    arr="$1"

Следовательно

#!/bin/bashfunction copyFiles() {   arr=("$@")   for i in "${arr[@]}";      do          echo "$i"      done}array=("one 1" "two 2" "three 3")copyFiles "${array[@]}"

Вывод таков (мой скрипт имеет имя foo)

$ ./foo   one 1two 2three 3

Если вы хотите передать один или несколько аргументов И массив, я предлагаю это изменение в скрипте @A.B.
Массив должен быть последний аргумент и единственный массив может быть передан

#!/bin/bashfunction 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[@]}"

Выход:

$ ./foo   Copying oneCopying twoCopying three

Вы также можете передать массив в качестве ссылки. т.е.:

#!/bin/bashfunction copyFiles {   local -n arr=$1   for i in "${arr[@]}"   do      echo "$i"   done}array=("one" "two" "three")copyFiles array

но обратите внимание, что любые изменения в arr будут внесены в array.

Есть пара проблем. Вот рабочая форма :

#!/bin/bashfunction copyFiles {   arr=( "$@" )   for i in "${arr[@]}";      do          echo "$i"      done}array=("one" "two" "three")copyFiles "${array[@]}"
  • Между объявлением функции и {

  • Вы не можете использовать $array, как array это массив, а не переменная. Если вы хотите получить все значения массива, используйте "${array[@]}"

  • В вашем объявлении основной функции вам нужно arr="$@" как "${array[@]}" будет расширен до индексированных значений, разделенных пробелами, если вы используете $1 вы получите только первое значение. Чтобы получить все значения, используйте arr="${arr[@]}".

Далее следует немного более крупный пример. Пояснения см. в комментариях к коду.

#!/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 elementsdeclare -a array_2=("Space separated words," sometimes using "string paretheses") # 4 elementsdeclare -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               Earray_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[@]}"

Лучший способ - передать в качестве аргументов позиции. Ничего больше. Вы можете передать как строку, но этот способ может вызвать некоторые проблемы. Пример:

array=(one two three four five)function show_passed_array(){  echo $@}

или

function show_passed_array(){  while $# -gt 0;do    echo $1;shift  done}    show_passed_array ${array[@]}

выход:

  one two three four five

>>Вы имеете в виду, что если значение массива содержит символы пробела, вы должны сначала заключить элементы в кавычки перед передачей для доступа к значению по индексу в функции use $1 $2 $ 3 ... position parameters. Где индекс 0 - 1, 1 - 2,... Для итерации доступа лучше всего использовать всегда $ 1 и после сдвига. Ничего дополнительного не требуется. Вы можете передавать аргументы без какого-либо массива, подобного этому:

show_passed_array one two three four five

bash media автоматически создает массив из переданных аргументов, который передает их в функцию, а затем у вас есть аргументы позиции.Кроме того, когда вы пишете ${array[2]}, вы действительно записываете последовательный аргумент один, два, три, четыре и передаете их функции.Так что эти вызовы эквивалентны.

Каким бы уродливым это ни было, вот обходной путь, который работает до тех пор, пока вы явно передаете не массив, а переменную, соответствующую массиву:

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

Я уверен, что кто-то может придумать более чистую реализацию этой идеи, но я обнаружил, что это лучшее решение, чем передача массива в виде "{array[@]"} а затем получить к нему внутренний доступ с помощью array_inside=("$@"). Это усложняется, когда существуют другие позиционные/getopts параметры. В этих случаях мне приходилось сначала определять, а затем удалять параметры, не связанные с массивом, используя некоторую комбинацию shift и удаление элементов массива.

С точки зрения пуристов, вероятно, такой подход рассматривается как нарушение языка, но с прагматической точки зрения этот подход избавил меня от множества огорчений. По смежной теме я также использую eval чтобы присвоить внутренне созданный массив переменной, названной в соответствии с параметром target_varname Я перехожу к функции:

eval $target_varname=$"(${array_inside[@]})"

Я предлагаю избежать каких-либо ограничений в отношении "передачи аргументов массива (ов)" в функцию путем ... передачи строк и превращения их в массив ВНУТРИ функции :

#!/bin/bashfalse_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}"

Использование ссылок (самый простой способ; лучший и единственный способ передачи ассоциативные массивы):

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

Для нормального (неассоциативный) массивы:

Вот функция, которая копирует переданный массив в новый (локальный) массив для использования. Может занять много времени для длинных массивов.

copy_fn(){    eval local newarr=\(\${$1[@]}\)    echo ${newarr[@]}}

Кроме того, как и в C++, вы можете передавать только указатель на массив, но в python в качестве общей переменной, которую вы также можете изменять.

Вот подход с общими переменными .. полезный для длинных массивов.

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 aecho ${a[@]}

-Химаншу

@SBF

Ваша идея вызвала некоторые размышления о ситуации, в которой я нуждался, когда нужно было сравнить два массива.

Вы можете передавать несколько массивов, используя свою технику с небольшими изменениями. Смотреть ниже

Спасибо!

(работает в bash 3.3 на 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[@]}"