Bagaimana cara melewatkan array sebagai argumen fungsi?

57

Berjuang sebentar melewati array sebagai argumen tapi tetap saja tidak berhasil. Saya sudah mencoba seperti di bawah ini:

#! /bin/bash

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

}

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

copyFiles $array

Jawaban dengan penjelasan akan menyenangkan.

Sunting: Pada dasarnya, saya akhirnya akan memanggil fungsi dari file skrip lain. Tolong jelaskan kendala jika memungkinkan.

Ahsanul Haque
sumber

Jawaban:

85
  • Memperluas array tanpa indeks hanya memberikan elemen pertama, gunakan

    copyFiles "${array[@]}"

    dari pada

    copyFiles $array
  • Gunakan bang-bang

    #!/bin/bash
  • Gunakan sintaks fungsi yang benar

    Varian yang valid adalah

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

    dari pada

    function copyFiles{…}
  • Gunakan sintaks yang tepat untuk mendapatkan parameter array

    arr=("$@")

    dari pada

    arr="$1"

Karena itu

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

}

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

copyFiles "${array[@]}"

Output adalah (skrip saya memiliki nama foo)

$ ./foo   
one
two
three
AB
sumber
terima kasih, tetapi bukankah fungsi copyFiles {...} sintaks yang benar? Meskipun saya seorang bie baru saya menjalankan beberapa program dengan sintaks.
Ahsanul Haque
Varian yang valid adalah copyFiles {…}dan copyFiles(){…}dan copyFiles() {…}, tetapi tidak copyFiles{…}. Perhatikan ruang dalam varian tanpa()
AB
19

Anda juga bisa meneruskan array sebagai referensi. yaitu:

#!/bin/bash

function copyFiles {
   local -n arr=$1

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

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

copyFiles array

tetapi perhatikan bahwa modifikasi apa pun untuk arr akan dilakukan ke array.


sumber
2
Meskipun, itu tidak persis apa yang saya inginkan, tetapi masih bagus untuk mengetahui cara kerja referensi melalui bash. +1 :)
Ahsanul Haque
4
Membutuhkan bash 4.3+
dtmland
19

Jika Anda ingin meneruskan satu atau lebih argumen DAN sebuah array, saya usulkan perubahan ini ke skrip @AB
Array harus menjadi argumen terakhir dan hanya satu array yang dapat dilewati

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

Keluaran:

$ ./foo   
Copying one
Copying two
Copying three
SBF
sumber
2
+1 untuk mempelajari tentang array yang harus ada di akhir dan hanya satu yang harus dikirim
David 'the botak jahe'
1
Terima kasih untuk shiftpenggunaannya.
Itachi
Ini juga berguna untuk menggunakan argumen shift, jadi jika Anda memiliki 6 argumen sebelum array, Anda bisa menggunakannya shift 6.
spinup
Anda mengonversi "sisa argumen" menjadi arr. Apakah mungkin untuk memiliki parameter array di tengah? Atau bahkan beberapa parameter array? function copyAndMove() { msg1=$1 ; arr1=...?... ; msg2=? ; arr2=...?... ; msg3=? ; ... }. Seperti saya akan mendefinisikannya dalam python: def copyAndMove(msg1="foo", cpFiles=[], msg2="bar", mvFiles=[], msg3="baz"): .... Sudahlah, saya menemukan stackoverflow.com/a/4017175/472245
towi
8

Ada beberapa masalah. Ini formulir kerjanya:

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

}

array=("one" "two" "three")
copyFiles "${array[@]}"
  • Harus ada setidaknya ruang antara deklarasi fungsi dan {

  • Anda tidak dapat menggunakan $array, seperti arrayarray bukan variabel. Jika Anda ingin mendapatkan semua nilai penggunaan array"${array[@]}"

  • Dalam Anda utama deklarasi fungsi Anda perlu arr="$@"sebagai "${array[@]}"akan memperluas dengan nilai-nilai diindeks dipisahkan oleh spasi, jika Anda menggunakan $1Anda akan mendapatkan hanya nilai pertama. Untuk mendapatkan semua nilai, gunakan arr="$arr[@]}".

heemayl
sumber
Anda perluarr=("$@")
AB
Untuk melihat perbedaannya, tambahkan di breakbawah ini echo "$i". Dalam versi Anda, Anda masih akan melihat semua elemen. Namun, itu harus tiga baris.
AB
@heemayl: kesalahan ketik kecil - {dalam array Anda dari peluru kedua hilang ... "$ {array [@]}" ...
Cbhihe
3

Berikut ini contoh yang sedikit lebih besar. Untuk penjelasan, lihat komentar dalam kode.

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

Cara terbaik adalah lulus sebagai argumen posisi. Tidak ada lagi. Anda dapat lulus sebagai string, tetapi cara ini dapat menyebabkan beberapa masalah. Contoh:

array=(one two three four five)

function show_passed_array(){
  echo $@
}

atau

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

    show_passed_array ${array[@]}

keluaran:

  one two three four five

Maksud Anda jika nilai array memiliki simbol spasi, Anda harus mengutip elemen terlebih dahulu sebelum lulus untuk mengakses nilai berdasarkan indeks dalam fungsi, gunakan $ 1 $ 2 $ 3 ... parameter posisi. Di mana indeks 0 -> 1, 1 -> 2, ... Untuk mengulangi akses yang terbaik adalah selalu menggunakan $ 1 dan setelah Shift. Tidak ada tambahan yang dibutuhkan. Anda dapat memberikan argumen tanpa array seperti ini:

show_passed_array one two three four five

bash media secara otomatis membuat larik dari argumen yang diteruskan yang meneruskannya agar berfungsi dan kemudian Anda memiliki argumen posisi. Selanjutnya ketika Anda menulis $ {array [2]} Anda benar-benar menulis argumen konsekuen satu dua tiga empat dan meneruskannya ke fungsi. Jadi, panggilan itu setara.

Anatoly
sumber
1

Seburuk itu, berikut adalah solusi yang berfungsi selama Anda tidak melewatkan array secara eksplisit, tetapi variabel yang sesuai dengan 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

Saya yakin seseorang dapat datang dengan implementasi ide yang lebih bersih, tetapi saya telah menemukan ini menjadi solusi yang lebih baik daripada melewati array "{array[@]"}dan kemudian mengaksesnya menggunakan internal array_inside=("$@"). Ini menjadi rumit ketika ada posisi / getoptsparameter lain. Dalam kasus ini, saya harus terlebih dahulu menentukan dan kemudian menghapus parameter yang tidak terkait dengan array menggunakan beberapa kombinasi shiftdan penghapusan elemen array.

Perspektif yang murni cenderung memandang pendekatan ini sebagai pelanggaran bahasa, tetapi secara pragmatis, pendekatan ini telah menyelamatkan saya dari banyak kesedihan. Pada topik terkait, saya juga menggunakan evaluntuk menetapkan array yang dibangun secara internal ke variabel bernama sesuai dengan parameter yang target_varnamesaya berikan ke fungsi:

eval $target_varname=$"(${array_inside[@]})"
Blake Schultze
sumber
Itu jelek dan tidak perlu. Jika Anda ingin lulus array dengan nama, membuat array_internallysebuah alias itu: declare -n array_internally=$1. Dan sisanya tentang "menjadi rumit" dan "menentukan dan kemudian menghapus ..." berlaku untuk terlepas dari bagaimana Anda melewati array, jadi saya tidak melihat apa gunanya itu. Dan evalarray yang berpotensi mengandung karakter khusus hanya menunggu kesedihan terjadi di kemudian hari.
muru