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

57

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

#! /bin/bash

function copyFiles{
   arr="$1"
   for i in "${arr[@]}";
      do
          echo "$i"
      done

}

array=("one" "two" "three")

copyFiles $array

Ответ с объяснением был бы хорош.

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

Ахсанул Хак
источник

Ответы:

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

    copyFiles "${array[@]}"

    вместо

    copyFiles $array
  • Используйте She-Bang

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

    Допустимые варианты

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

    вместо

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

    arr=("$@")

    вместо

    arr="$1"

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

#!/bin/bash
function copyFiles() {
   arr=("$@")
   for i in "${arr[@]}";
      do
          echo "$i"
      done

}

array=("one" "two" "three")

copyFiles "${array[@]}"

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

$ ./foo   
one
two
three
AB
источник
спасибо, но разве функция copyFiles {…} не имеет правильный синтаксис? Хотя я новичок, я успешно запускаю программу с синтаксисом.
Ахсанул Хак
Допустимые варианты есть copyFiles {…}и copyFiles(){…}и copyFiles() {…}, но нет copyFiles{…}. Обратите внимание на пробел в варианте без()
AB
19

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

#!/bin/bash

function copyFiles {
   local -n arr=$1

   for i in "${arr[@]}"
   do
      echo "$i"
   done
}

array=("one" "two" "three")

copyFiles array

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


источник
2
Хотя это было не совсем то, чего я хочу, но все же приятно знать, как работает передача по ссылке в bash. +1 :)
Ахсанул Хаке
4
Требуется bash 4.3+
dtmland
19

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

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

Выход:

$ ./foo   
Copying one
Copying two
Copying three
SBF
источник
2
+1 за информацию о массиве, который должен быть в конце, и о том, что должен быть отправлен только один
Дэвид «лысый имбирь»
1
Спасибо за shiftиспользование.
Итачи
Иногда полезно использовать аргумент shift, поэтому, если перед массивом было 6 аргументов, вы можете использовать его shift 6.
раскрутка
Вы конвертируете «остальные аргументы» в arr. Возможно ли иметь параметр массива в середине? Или даже несколько параметров массивов? function copyAndMove() { msg1=$1 ; arr1=...?... ; msg2=? ; arr2=...?... ; msg3=? ; ... }, Как я бы определил его в Python: def copyAndMove(msg1="foo", cpFiles=[], msg2="bar", mvFiles=[], msg3="baz"): .... Неважно, я нашел stackoverflow.com/a/4017175/472245
Towi
8

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

#!/bin/bash
function copyFiles {
   arr=( "$@" )
   for i in "${arr[@]}";
      do
          echo "$i"
      done

}

array=("one" "two" "three")
copyFiles "${array[@]}"
  • Должен быть как минимум пробел между объявлением функции и {

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

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

heemayl
источник
Вам нужноarr=("$@")
AB
Чтобы увидеть разницу, добавьте breakниже echo "$i". В вашей версии вы все равно увидите все элементы. Однако должно быть три строчки.
AB
@heemayl: маленькая опечатка - {в вашем массиве второй пули пропал без вести ... "$ {array [@]}" ...
Cbhihe
3

Здесь следует чуть более крупный пример. Для объяснения смотрите комментарии в коде.

#!/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[@]}"
Ульф Ореборн
источник
1

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

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

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

show_passed_array one two three four five

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

Анатолий
источник
1

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

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[@]})"
Блейк Шульце
источник
Это безобразно и не нужно. Если вы хотите передать массив по имени, сделать array_internallyпсевдоним этого: declare -n array_internally=$1. А остальная часть о «усложняется» и «определять, а затем удалять ...» применяется независимо от того, как вы передаете массив, так что я не вижу смысла в этом. А evalсоздание массива, потенциально содержащего специальные символы, просто ожидает скорби в будущем.
Муру