Bir diziyi işlev argümanı olarak nasıl iletirim?


57

Bir dizi mücadele, bir diziyi argüman olarak geçirme ama yine de çalışmıyor. Aşağıdaki gibi denedim:

#! /bin/bash

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

}

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

copyFiles $array

Açıklamalı bir cevap iyi olurdu.

Düzenleme: Temelde, sonunda işlevi başka bir komut dosyasından arayacağım. Plz mümkünse kısıtlamaları açıklar.

Yanıtlar:


84
  • Dizini bir dizini kullanmadan genişletmek yalnızca ilk elemanı verir;

    copyFiles "${array[@]}"

    onun yerine

    copyFiles $array
  • She-bang kullan

    #!/bin/bash
  • Doğru fonksiyon sözdizimini kullanın

    Geçerli değişkenler

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

    onun yerine

    function copyFiles{…}
  • Dizi parametresini almak için doğru sözdizimini kullanın

    arr=("$@")

    onun yerine

    arr="$1"

bu nedenle

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

}

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

copyFiles "${array[@]}"

Çıktı (betiğimde adı var foo)

$ ./foo   
one
two
three

teşekkürler, ancak işlev copyFiles {…} doğru bir sözdizimi değil mi? Yeni bir bie olsam da bazı programları sözdizimi ile başarılı bir şekilde yürütüyorum.
Ahsanul Haque 15:15

Geçerli değişkenler copyFiles {…}ve copyFiles(){…}ve copyFiles() {…}, ancak değildir copyFiles{…}. ()
AB

19

Bir ya da daha fazla argüman ve bir dizi iletmek istiyorsanız, bu değişikliğin @ ab dizgesindeki betiğe son argüman
olması gerektiğini ve sadece bir dizinin geçilebileceğini öneriyorum.

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

Çıktı:

$ ./foo   
Copying one
Copying two
Copying three

2
Sonunda olması gereken ve yalnızca birinin gönderilmesi gerektiğini belirten bir dizi hakkında bilgi edinmek için +1
David 'kel zencefil'

1
shiftKullanım için teşekkürler .
Itachi

Ayrıca bazen vardiya argümanını kullanmakta fayda vardır, bu yüzden diziden önce 6 argüman varsa, kullanabilirsiniz shift 6.
spinup

"Argümanların geri kalanını" içine dönüştürürsünüz arr. Ortada bir dizi parametresi olması mümkün mü? Veya hatta birkaç dizi parametresi? function copyAndMove() { msg1=$1 ; arr1=...?... ; msg2=? ; arr2=...?... ; msg3=? ; ... }. Gibi Python tanımlarsınız: def copyAndMove(msg1="foo", cpFiles=[], msg2="bar", mvFiles=[], msg3="baz"): .... Aldırma, stackoverflow.com/a/4017175/472245
towi,

18

Diziyi bir referans olarak da iletebilirsiniz. yani:

#!/bin/bash

function copyFiles {
   local -n arr=$1

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

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

copyFiles array

ancak, arr üzerinde yapılacak herhangi bir değişikliğin diziye yapılacağına dikkat edin.


1
Yine de istediğim tam olarak değildi, ama yine de referansta bash ile nasıl çalışıldığını bilmek güzel. +1 :)
Ahsanul Haque 15:15

3
Bash
4.3+

8

Birkaç sorun var. İşte çalışma şekli:

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

}

array=("one" "two" "three")
copyFiles "${array[@]}"
  • En azından işlev bildirimi ile işlev bildirimi arasında bir boşluk olması gerekir. {

  • Sen kullanamazsınız $arraygibi arraybir dizi değil bir değişkendir. Bir dizi kullanımının tüm değerlerini almak istiyorsanız"${array[@]}"

  • Ana fonksiyon bildiriminde boşluklarla ayrılmış indekslenmiş değerlere genişleyeceğiniz arr="$@"gibi ihtiyacınız "${array[@]}"olacak, eğer kullanırsanız $1sadece ilk değeri elde edeceksiniz. Tüm değerleri kullanmak için kullanın arr="$arr[@]}".


İhtiyacınız vararr=("$@")
AB

Farkı görmek için bir ekleme breakaşağıda echo "$i". Sürümünüzde hala tüm unsurları göreceksiniz. Ancak, üç satır olmalıdır.
AB

@heemayl: küçük yazım hatası - {ikinci merminin dizisindeki dizininiz kayboldu ... "$ {array [@]}" ...
15'de

3

İşte biraz daha büyük bir örnek izler. Açıklama için koddaki açıklamalara bakınız.

#!/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

En iyi yol pozisyon argümanları olarak geçmektir. Başka hiçbir şey. Dize olarak geçebilirsiniz, ancak bu şekilde bazı sıkıntılara neden olabilir. Örnek:

array=(one two three four five)

function show_passed_array(){
  echo $@
}

veya

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

    show_passed_array ${array[@]}

çıktı:

  one two three four five

Dizi değerinde boşluk sembolleri varsa, işlevde dizine göre dizine göre erişim için önce $ 1 $ 2 $ 3 ... position parametreleri kullanın. Dizin 0 -> 1, 1 -> 2, ... Erişimi tekrarlamak için her zaman $ 1 ve Shift'ten sonra kullanmak en iyisidir. Ek bir şey gerekmez. Böyle bir dizi olmadan değişkenleri iletebilirsiniz:

show_passed_array one two three four five

bash media, otomatik olarak onları işlevine geçen argümanlardan bir dizi oluşturur ve sonra konum argümanlarına sahipsiniz. Ayrıca, $ {array [2]} yazdığınızda, gerçekten iki iki dört argüman yazıp bunları fonksiyona geçirirsiniz. Yani bu çağrılar eşdeğerdir.


1

Çirkin olduğu gibi, burada açıkça bir diziyi geçmediğiniz, ancak bir diziye karşılık gelen bir değişkeni geçmediğiniz sürece çalışan bir geçici çözüm bulunmaktadır:

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

Birinin fikrinin daha temiz bir uygulamasını bulabileceğinden eminim, ancak bunu bir diziyi geçirmekten "{array[@]"}ve sonra da onu kullanarak kullanmaktan daha iyi bir çözüm buldum array_inside=("$@"). Başka konumsal / getoptsparametreler olduğunda bu karmaşıklaşır . Bu durumlarda, önce bazı kombinasyonları shiftve dizi elemanlarının kaldırılmasını kullanarak dizi ile ilişkili olmayan parametreleri belirlemek ve kaldırmak zorunda kaldım .

Saf bir bakış açısı bu yaklaşımı dilin ihlali olarak görür, ancak pratik olarak konuşursak, bu yaklaşım beni çok fazla üzdü. İlgili bir konuda, işleve ilettiğim evalparametreye göre adlandırılmış bir değişkene dahili olarak oluşturulan bir dizi atamak için de kullanıyorum target_varname:

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

Bu çirkin ve gereksiz. Adıyla dizisi geçirmek istiyorsanız, yapmak array_internallybunun bir takma ad: declare -n array_internally=$1. Ve "karmaşık hale gelir" ve "belirle ve sonra kaldır ..." hakkındaki geri kalanlar diziyi nasıl geçirdiğinize bakılmaksızın uygulanır, bu yüzden bunun ne olduğunu göremiyorum. Ve evalkeder daha sonraki bir tarihte gerçekleşmesi için özel karakterleri içeren potansiyel bir dizi ing sadece bekliyor.
muru
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.