Como passar uma matriz como argumento de função?

57

Lutando por um tempo passando uma matriz como argumento, mas não está funcionando de qualquer maneira. Eu tentei como abaixo:

#! /bin/bash

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

}

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

copyFiles $array

Uma resposta com explicação seria legal.

Edit: Basicamente, acabarei chamando a função de outro arquivo de script. Por favor, explique as restrições, se possível.

Ahsanul Haque
fonte

Respostas:

85
  • A expansão de uma matriz sem um índice fornece apenas o primeiro elemento, use

    copyFiles "${array[@]}"

    ao invés de

    copyFiles $array
  • Use um she-bang

    #!/bin/bash
  • Use a sintaxe da função correta

    Variantes válidas são

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

    ao invés de

    function copyFiles{…}
  • Use a sintaxe correta para obter o parâmetro da matriz

    arr=("$@")

    ao invés de

    arr="$1"

Portanto

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

}

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

copyFiles "${array[@]}"

Saída é (meu script tem o nome foo)

$ ./foo   
one
two
three
AB
fonte
obrigado, mas a função copyFiles {…} não é uma sintaxe correta? Embora eu seja um novato, eu corro algum programa com êxito com a sintaxe.
Ahsanul Haque 15/09/2015
Variantes válidas são copyFiles {…}e copyFiles(){…}e copyFiles() {…}, mas não copyFiles{…}. Observe o espaço na variante sem()
AB
19

Você também pode passar a matriz como referência. ou seja:

#!/bin/bash

function copyFiles {
   local -n arr=$1

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

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

copyFiles array

mas observe que quaisquer modificações no arr serão feitas no array.


fonte
2
No entanto, não era exatamente o que eu queria, mas ainda assim é bom saber como passar por referência funciona no bash. +1 :)
Ahsanul Haque 15/09/2015
4
Requer bash 4.3+
dtmland
19

Se você deseja passar um ou mais argumentos E uma matriz, proponho que essa alteração no script da @AB
Matriz seja o último argumento e apenas uma matriz possa ser transmitida.

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

Resultado:

$ ./foo   
Copying one
Copying two
Copying three
SBF
fonte
2
+1 para aprender sobre uma matriz que precisa estar no final e que apenas uma deve ser enviada
David 'o careca'
11
Obrigado pelo shiftuso.
Itachi
Também é útil usar o argumento de shift às vezes; portanto, se você tiver 6 argumentos antes da matriz, poderá usá-lo shift 6.
spinup 04/07
Você converte "o restante dos argumentos" em arr. É possível ter um parâmetro de matriz no meio? Ou mesmo vários parâmetros de matrizes? function copyAndMove() { msg1=$1 ; arr1=...?... ; msg2=? ; arr2=...?... ; msg3=? ; ... }. Como eu definiria-lo em python: def copyAndMove(msg1="foo", cpFiles=[], msg2="bar", mvFiles=[], msg3="baz"): .... Não importa, eu encontrei stackoverflow.com/a/4017175/472245
towi 25/10
8

Existem alguns problemas. Aqui está a forma de trabalho:

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

}

array=("one" "two" "three")
copyFiles "${array[@]}"
  • Deve haver pelo menos um espaço entre a declaração da função e {

  • Você não pode usar $array, como arrayé uma matriz, não uma variável. Se você deseja obter todos os valores de uma matriz, use"${array[@]}"

  • Em você principal declaração da função que você precisa arr="$@"como "${array[@]}"vai se expandir para os valores indexados separados por espaços, se você usar $1você obteria apenas o primeiro valor. Para obter todos os valores, use arr="$arr[@]}".

heemail
fonte
Você precisaarr=("$@")
AB
Para ver a diferença, adicione um breakabaixo echo "$i". Na sua versão, você ainda verá todos os elementos. No entanto, deve haver três linhas.
AB
@heemayl: pequeno erro de digitação - O {no seu array da segunda bala desapareceu ... "$ {array [@]}" ...
#
3

Aqui segue um exemplo um pouco maior. Para explicação, consulte os comentários no código.

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

A melhor maneira é passar como argumentos de posição. Nada mais. Você pode passar como string, mas dessa maneira pode causar alguns problemas. Exemplo:

array=(one two three four five)

function show_passed_array(){
  echo $@
}

ou

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

    show_passed_array ${array[@]}

resultado:

  one two three four five

Você quer dizer que se o valor da matriz possui símbolos de espaço, você deve citar os elementos antes de passar para acessar o valor pelo índice na função, usar parâmetros de posição $ 1 $ 2 $ 3 .... Onde o índice 0 -> 1, 1 -> 2, ... Para iterar o acesso, é melhor usar sempre $ 1 e depois do Shift. Nada adicional é necessário. Você pode passar argumentos sem nenhuma matriz como esta:

show_passed_array one two three four five

bash media cria automaticamente uma matriz a partir de argumentos passados ​​que os passaram a funcionar e, em seguida, você tem argumentos de posição. Além disso, quando você escreve $ {array [2]}, realmente escreve o argumento consequente um dois três quatro e os passa para a função. Portanto, essas chamadas são equivalentes.

Anatoly
fonte
1

Por mais feia que seja, aqui está uma solução alternativa que funciona desde que você não esteja passando uma matriz explicitamente, mas uma variável correspondente a uma matriz:

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

Tenho certeza de que alguém pode sugerir uma implementação mais limpa da ideia, mas achei que essa é uma solução melhor do que passar uma matriz "{array[@]"}e acessá-la internamente usando array_inside=("$@"). Isso se torna complicado quando existem outros getoptsparâmetros / posicionais . Nesses casos, tive que primeiro determinar e remover os parâmetros não associados à matriz usando alguma combinação de shiftremoção e elemento da matriz.

Uma perspectiva purista provavelmente vê essa abordagem como uma violação da linguagem, mas falando pragmaticamente, essa abordagem me salvou de muita tristeza. Em um tópico relacionado, eu também uso evalpara atribuir uma matriz construída internamente a uma variável nomeada de acordo com um parâmetro que target_varnameeu passo para a função:

eval $target_varname=$"(${array_inside[@]})"
Blake Schultze
fonte
Isso é feio e desnecessário. Se você quiser passar a matriz pelo nome, fazer array_internallyum apelido para ele: declare -n array_internally=$1. E o resto sobre "se torna complicado" e "determina e remove ..." se aplica independentemente de como você passa a matriz, então não vejo qual é o sentido disso. E evaluma matriz potencialmente contendo caracteres especiais está apenas aguardando o luto acontecer mais tarde.
muru 12/07/19