Jak przekazać tablicę jako argument funkcji?

57

Walczy przez chwilę, przekazując tablicę jako argument, ale i tak nie działa. Próbowałem jak poniżej:

#! /bin/bash

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

}

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

copyFiles $array

Odpowiedź z wyjaśnieniem byłaby miła.

Edycja: Zasadniczo w końcu wywołam funkcję z innego pliku skryptu. Plz wyjaśni, jeśli to możliwe, ograniczenia.

Ahsanul Haque
źródło

Odpowiedzi:

84
  • Rozszerzenie tablicy bez indeksu daje tylko pierwszy element, użyj

    copyFiles "${array[@]}"

    zamiast

    copyFiles $array
  • Użyj she-bang

    #!/bin/bash
  • Użyj poprawnej składni funkcji

    Prawidłowe warianty to

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

    zamiast

    function copyFiles{…}
  • Użyj właściwej składni, aby uzyskać parametr tablicy

    arr=("$@")

    zamiast

    arr="$1"

W związku z tym

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

}

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

copyFiles "${array[@]}"

Dane wyjściowe to (mój skrypt ma nazwę foo)

$ ./foo   
one
two
three
AB
źródło
dzięki, ale czy funkcja copyFiles {…} nie jest poprawną składnią? Chociaż jestem nowym bie, z powodzeniem uruchamiam jakiś program ze składnią.
Ahsanul Haque,
Poprawne są następujące warianty copyFiles {…}i copyFiles(){…}a copyFiles() {…}, ale nie copyFiles{…}. Zwróć uwagę na miejsce w wariancie bez()
AB
19

Jeśli chcesz przekazać jeden lub więcej argumentów ORAZ tablicę, proponuję tę zmianę w skrypcie @AB
Tablica powinna być ostatnim argumentem i można przekazać tylko jedną tablicę

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

Wynik:

$ ./foo   
Copying one
Copying two
Copying three
SBF
źródło
2
+1 za poznanie tablicy, która musi znajdować się na końcu i że należy wysłać tylko jedną
David „łysy imbir”
1
Dzięki za shiftkorzystanie.
Itachi
Przydatne jest również czasami użycie argumentu shift, więc jeśli miałeś 6 argumentów przed tablicą, możesz użyć shift 6.
spinup
Konwertujesz „resztę argumentów” na arr. Czy można mieć parametr tablicy pośrodku? A może nawet kilka parametrów tablic? function copyAndMove() { msg1=$1 ; arr1=...?... ; msg2=? ; arr2=...?... ; msg3=? ; ... }. Jak ja bym zdefiniował je w Pythonie def copyAndMove(msg1="foo", cpFiles=[], msg2="bar", mvFiles=[], msg3="baz"): .... Nieważne, znalazłem stackoverflow.com/a/4017175/472245
towi
18

Możesz także przekazać tablicę jako odniesienie. to znaczy:

#!/bin/bash

function copyFiles {
   local -n arr=$1

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

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

copyFiles array

ale zauważ, że wszelkie modyfikacje arr zostaną wprowadzone w tablicy.


źródło
1
Chociaż nie było to dokładnie to, czego chcę, ale miło jest wiedzieć, jak przejść przez referencję w bash. +1 :)
Ahsanul Haque,
3
Wymaga bash 4.3+
dtmland
8

Jest kilka problemów. Oto formularz roboczy:

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

}

array=("one" "two" "three")
copyFiles "${array[@]}"
  • Między deklaracją funkcji a musi być co najmniej spacja {

  • Nie możesz używać $array, ponieważ arraytablica nie jest zmienną. Jeśli chcesz uzyskać wszystkie wartości z tablicy, użyj"${array[@]}"

  • W głównej deklaracji funkcji potrzebujesz, arr="$@"jak "${array[@]}"będzie rozwinięte do wartości indeksowanych oddzielonych spacjami, jeśli użyjesz $1, dostaniesz tylko pierwszą wartość. Aby uzyskać wszystkie wartości, użyj arr="$arr[@]}".

heemayl
źródło
Potrzebujeszarr=("$@")
AB
Aby zobaczyć różnicę, dodaj breakponiżej echo "$i". W swojej wersji nadal zobaczysz wszystkie elementy. Jednak powinny to być trzy linie.
AB
@heemayl: mała literówka - {w Twojej tablicy drugiej kuli zaginął ... "$ {array [@]}" ...
Cbhihe
3

Oto nieco większy przykład. Aby uzyskać wyjaśnienie, zobacz komentarze w kodzie.

#!/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
źródło
1

Najlepszym sposobem jest podanie jako argumentów pozycji. Nic więcej. Możesz podać jako ciąg, ale w ten sposób może powodować pewne problemy. Przykład:

array=(one two three four five)

function show_passed_array(){
  echo $@
}

lub

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

    show_passed_array ${array[@]}

wynik:

  one two three four five

Masz na myśli, że jeśli wartość tablicy ma symbole spacji, musisz podać elementy przed przejściem, aby uzyskać dostęp do wartości przez indeks w funkcji, użyj parametrów pozycji 1 $ 2 $ 3 $ ... Gdzie indeks 0 -> 1, 1 -> 2, ... Aby iterować dostęp, najlepiej użyć zawsze 1 $ i po Shift. Nic więcej nie jest potrzebne. Możesz przekazać argumenty bez takiej tablicy:

show_passed_array one two three four five

bash media automatycznie buduje tablicę z przekazanych argumentów, które przekazały je do funkcji, a następnie masz argumenty pozycji. Ponadto, kiedy piszesz $ {array [2]}, naprawdę piszesz konsekwentny argument jeden dwa trzy cztery i przekazujesz je do funkcji. Te połączenia są równoważne.

Anatolij
źródło
1

Choć jest to brzydkie, oto obejście, które działa, o ile nie przekazujesz tablicy wprost, ale zmienna odpowiadająca tablicy:

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

Jestem pewien, że ktoś może wymyślić czystszą implementację tego pomysłu, ale uważam, że jest to lepsze rozwiązanie niż przekazywanie tablicy, "{array[@]"}a następnie uzyskiwanie do niej wewnętrznego dostępu array_inside=("$@"). Staje się to skomplikowane, gdy istnieją inne pozycje / getoptsparametry. W takich przypadkach musiałem najpierw określić, a następnie usunąć parametry niezwiązane z tablicą, używając kombinacji shiftusuwania elementu tablicy.

Perspektywa purystyczna prawdopodobnie postrzega to podejście jako pogwałcenie języka, ale pragmatycznie rzecz biorąc, to podejście uratowało mi wiele smutku. W pokrewnym temacie używam również evaldo przypisywania wewnętrznie skonstruowanej tablicy do zmiennej o nazwie zgodnej z parametrem target_varnameprzekazywanym do funkcji:

eval $target_varname=$"(${array_inside[@]})"
Blake Schultze
źródło
To jest brzydkie i niepotrzebne. Jeśli chcesz przekazać tablicę według nazwy, należy array_internallyalias z nim: declare -n array_internally=$1. Reszta o „komplikowaniu” i „określaniu, a następnie usuwaniu ...” ma zastosowanie niezależnie od tego, jak przekazujesz tablicę, więc nie rozumiem, o co w tym wszystkim chodzi. A evaltablica potencjalnie zawierająca znaki specjalne czeka tylko na smutek w późniejszym terminie.
muru