Come passare un array come argomento di funzione?


57

Lottando per un po 'passando un array come argomento, ma non funziona comunque. Ho provato come di seguito:

#! /bin/bash

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

}

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

copyFiles $array

Una risposta con una spiegazione sarebbe piacevole.

Modifica: sostanzialmente, alla fine chiamerò la funzione da un altro file di script. Per favore, spiega i vincoli, se possibile.

Risposte:


85
  • L'espansione di un array senza un indice fornisce solo il primo elemento, utilizzare

    copyFiles "${array[@]}"

    invece di

    copyFiles $array
  • Usa un botto

    #!/bin/bash
  • Utilizzare la sintassi della funzione corretta

    Le varianti valide sono

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

    invece di

    function copyFiles{…}
  • Utilizzare la sintassi corretta per ottenere il parametro array

    arr=("$@")

    invece di

    arr="$1"

Perciò

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

}

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

copyFiles "${array[@]}"

L'output è (il mio script ha il nome foo)

$ ./foo   
one
two
three

grazie, ma la funzione copyFiles {…} non è una sintassi corretta? Anche se sono un nuovo amico, eseguo con successo un programma con la sintassi.
Ahsanul Haque,

Le varianti valide sono copyFiles {…}e copyFiles(){…}e copyFiles() {…}, ma non copyFiles{…}. Nota lo spazio nella variante senza()
AB

19

È inoltre possibile passare l'array come riferimento. vale a dire:

#!/bin/bash

function copyFiles {
   local -n arr=$1

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

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

copyFiles array

ma nota che eventuali modifiche ad arr verranno apportate all'array.


2
Tuttavia, non era esattamente quello che volevo, ma è comunque bello sapere come funzionano i riferimenti in bash. +1 :)
Ahsanul Haque,

4
Richiede bash 4.3+
dtmland

19

Se si desidera passare uno o più argomenti E un array, propongo questa modifica allo script di @AB
Array dovrebbe essere l' ultimo argomento e solo un array può essere passato

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

Produzione:

$ ./foo   
Copying one
Copying two
Copying three

2
+1 per conoscere un array che deve essere alla fine e che deve essere inviato solo uno
David "the bald ginger",

1
Grazie per l' shiftuso
Itachi,

È anche utile usare l'argomento shift a volte, quindi se hai avuto 6 argomenti prima dell'array, puoi usare shift 6.
spinup

Converti "il resto degli argomenti" in arr. È possibile avere un parametro array nel mezzo? O anche diversi parametri di array? function copyAndMove() { msg1=$1 ; arr1=...?... ; msg2=? ; arr2=...?... ; msg3=? ; ... }. Come Lo definirei in python: def copyAndMove(msg1="foo", cpFiles=[], msg2="bar", mvFiles=[], msg3="baz"): .... Non importa, ho trovato stackoverflow.com/a/4017175/472245
Towi

8

Ci sono un paio di problemi. Ecco la forma di lavoro:

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

}

array=("one" "two" "three")
copyFiles "${array[@]}"
  • È necessario almeno uno spazio tra la dichiarazione di funzione e {

  • Non è possibile utilizzare $array, in quanto arrayun array non è una variabile. Se si desidera ottenere tutti i valori di un array, utilizzare"${array[@]}"

  • Nella dichiarazione della funzione principale è necessario in arr="$@"quanto "${array[@]}"si espanderà ai valori indicizzati separati da spazi, se si utilizza $1si otterrebbe solo il primo valore. Per ottenere tutti i valori utilizzare arr="$arr[@]}".


Ti servearr=("$@")
AB

Per vedere la differenza, aggiungi un breaksotto echo "$i". Nella tua versione vedrai ancora tutti gli elementi. Tuttavia, dovrebbe essere di tre righe.
AB,

@heemayl: piccolo errore di battitura - Il {nel tuo array del secondo proiettile è scomparso ... "$ {array [@]}" ...
Cbhihe

3

Ecco un esempio leggermente più grande. Per una spiegazione, vedere i commenti nel codice.

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

Il modo migliore è passare come argomenti di posizione. Nient'altro. Puoi passare come stringa, ma in questo modo potresti causare alcuni problemi. Esempio:

array=(one two three four five)

function show_passed_array(){
  echo $@
}

o

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

    show_passed_array ${array[@]}

produzione:

  one two three four five

Intendi che se il valore dell'array ha simboli di spazio, devi prima citare gli elementi prima di passare per accedere al valore in base all'indice nella funzione usa $ 1 $ 2 $ 3 ... parametri di posizione. Dove indice 0 -> 1, 1 -> 2, ... Per iterare l'accesso è meglio usare sempre $ 1 e dopo Shift. Non è necessario altro. È possibile passare argomenti senza alcun array come questo:

show_passed_array one two three four five

bash media crea automaticamente un array dagli argomenti passati che li hanno passati alla funzione e quindi si hanno argomenti di posizione. Inoltre quando scrivi $ {array [2]} scrivi davvero l'argomento conseguente uno due tre quattro e li passi alla funzione. Quindi quelle chiamate sono equivalenti.


1

Per quanto brutta sia, ecco una soluzione alternativa che funziona finché non si passa esplicitamente un array, ma una variabile corrispondente a un 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

Sono sicuro che qualcuno può escogitare un'implementazione più pulita dell'idea, ma ho trovato che questa è una soluzione migliore rispetto al passaggio di un array "{array[@]"}e al suo accesso interno mediante array_inside=("$@"). Questo diventa complicato quando ci sono altri getoptsparametri / posizionali . In questi casi, ho dovuto prima determinare e quindi rimuovere i parametri non associati all'array usando una combinazione di shifte rimozione dell'elemento dell'array.

Una prospettiva purista probabilmente vede questo approccio come una violazione della lingua, ma parlando pragmaticamente, questo approccio mi ha risparmiato molto dolore. Su un argomento correlato, utilizzo anche evalper assegnare una matrice costruita internamente a una variabile denominata in base a un parametro target_varnameche passo alla funzione:

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

È brutto e inutile. Se si vuole passare la matrice in base al nome, fare array_internallyun alias di esso: declare -n array_internally=$1. E il resto di "diventa complicato" e "determina e poi rimuovi ..." si applica indipendentemente da come si passa l'array, quindi non vedo quale sia il punto. E la evalcreazione di un array potenzialmente contenente caratteri speciali sta solo aspettando che il dolore si verifichi in un secondo momento.
muru,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.