Як передати масив як аргумент функції?


57

Деякий час бореться, передаючи масив як аргумент, але він все одно не працює. Я спробував, як нижче:

#! /bin/bash

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

}

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

copyFiles $array

Відповідь із поясненням була б непогана.

Редагувати: В основному я з часом викличу функцію з іншого файлу сценарію. Plz поясніть обмеження, якщо це можливо.

Відповіді:


84
  • Розширення масиву без індексу дає лише перший елемент, використання

    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

дякую, але чи не є функція copyFiles {…} правильним синтаксисом? Хоча я новий бій, я запускаю якусь програму успішно із синтаксисом.
Ahsanul Haque

Допустимі варіанти copyFiles {…}і copyFiles(){…}та copyFiles() {…}, але не copyFiles{…}. Зверніть увагу на прогалину в варіанті без()
AB

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

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

18

Ви також можете передати масив як орієнтир. тобто:

#!/bin/bash

function copyFiles {
   local -n arr=$1

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

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

copyFiles array

але зауважте, що будь-які зміни arr будуть вноситись до масиву.


1
Хоча, це було не зовсім те, що я хочу, але все одно приємно знати, як пройти по довідковій роботі в баші. +1 :)
Ahsanul Haque

3
Потрібен bash 4.3+
dtmland

8

Є кілька проблем. Ось робоча форма:

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

}

array=("one" "two" "three")
copyFiles "${array[@]}"
  • Повинно бути принаймні пробіл між оголошенням функції та {

  • Ви не можете використовувати $array, оскільки arrayмасив не є змінною. Якщо ви хочете отримати всі значення використання масиву"${array[@]}"

  • У вас основне оголошення функції, яке вам потрібно, arr="$@"як "${array[@]}"розшириться до індексованих значень, розділених пробілами, якщо ви використовуєте, $1ви отримаєте лише перше значення. Щоб отримати всі значення, використовуйте arr="$arr[@]}".


Вам потрібноarr=("$@")
AB

Щоб побачити різницю, додайте breakнижче echo "$i". У вашій версії ви все одно побачите всі елементи. Однак це повинно бути три лінії.
AB

@heemayl: small typo - The {у вашому масиві другої кулі зник безвісти ... "$ {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 .... Якщо індекс 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якщо масив, який потенційно містить спеціальних символів, просто чекає, коли горе станеться пізніше.
муру
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.