Como posso passar uma matriz como parâmetro para uma função bash?
Nota: Depois de não encontrar uma resposta aqui no Stack Overflow, publiquei minha solução um tanto grosseira. Ele permite que apenas uma matriz seja passada e seja o último elemento da lista de parâmetros. Na verdade, ele não está transmitindo a matriz, mas uma lista de seus elementos, que são remontados em uma matriz por named_function (), mas funcionou para mim. Se alguém souber uma maneira melhor, fique à vontade para adicioná-lo aqui.
Respostas:
Você pode passar várias matrizes como argumentos usando algo como isto:
ecoará:
Editar / notas: (dos comentários abaixo)
descTable
eoptsTable
são passados como nomes e são expandidos na função Portanto, não$
é necessário quando fornecido como parâmetros.descTable
etc sendo definido comlocal
, porque os locais são visíveis para as funções que chamam.!
in${!1}
expande a variável arg 1.declare -a
apenas torna explícita a matriz indexada, não é estritamente necessário.fonte
Nota: Esta é a solução um tanto grosseira que eu postei, depois de não encontrar uma resposta aqui no Stack Overflow. Ele permite que apenas uma matriz seja passada e seja o último elemento da lista de parâmetros. Na verdade, ele não está transmitindo a matriz, mas uma lista de seus elementos, que são remontados em uma matriz por named_function (), mas funcionou para mim. Um pouco mais tarde, Ken postou sua solução, mas eu mantive a minha aqui para referência "histórica".
Melhorado pelo TheBonsai, obrigado.
fonte
called_function "${#array[@]}" "${array[@]}" "${#array2[@]}" "${array2[@]}"
etc ... ainda com algumas restrições óbvias, mas realmente melhor resolver o problema da maneira que o idioma suportar, em vez de tentar dobrar o idioma para que ele funcione da maneira que você está acostumado em outros idiomas.Comentando a solução de Ken Bertelson e respondendo a Jan Hettich:
Como funciona
a função
takes_ary_as_arg descTable[@] optsTable[@]
line intry_with_local_arys()
envia:descTable
eoptsTable
que são acessíveis àtakes_ary_as_arg
função.takes_ary_as_arg()
função recebedescTable[@]
eoptsTable[@]
como strings, isso significa$1 == descTable[@]
e$2 == optsTable[@]
.no início da
takes_ary_as_arg()
função, ele usa a${!parameter}
sintaxe, que é chamada de referência indireta ou, às vezes, dupla referência , isso significa que, em vez de usar$1
o valor de, usamos o valor do valor expandido de$1
, por exemplo:da mesma forma para
$2
.argAry1=("${!1}")
criaargAry1
como uma matriz (os colchetes a seguir=
) com o expandidodescTable[@]
, assim como escreverargAry1=("${descTable[@]}")
diretamente nele . odeclare
não é necessário.NB: Vale ressaltar que a inicialização do array usando este formulário de colchete inicializa o novo array de acordo com o
IFS
ou Internal Field Separator, que é, por padrão , guia , nova linha e espaço . nesse caso, como usava a[@]
notação, cada elemento é visto por si só como se tivesse sido citado (ao contrário de[*]
).Minha reserva com ele
Em
BASH
, escopo de variável local é a função atual e todas as funções filho são chamadas a partir dele, isso se traduz no fato de que atakes_ary_as_arg()
função "vê" essesdescTable[@]
eoptsTable[@]
arrays, portanto está funcionando (veja a explicação acima).Sendo esse o caso, por que não olhar diretamente para essas variáveis? É como escrever lá:
Veja a explicação acima, que apenas copia
descTable[@]
os valores da matriz de acordo com a correnteIFS
.Em suma
Também quero enfatizar o comentário de Dennis Williamson acima: matrizes esparsas (matrizes sem todas as chaves definidas - com "orifícios" nelas) não funcionarão conforme o esperado - perderíamos as chaves e "condensaríamos" a matriz.
Dito isto, vejo o valor da generalização, pois as funções podem obter as matrizes (ou cópias) sem saber os nomes:
para cópias reais: podemos usar uma avaliação para as chaves, por exemplo:
e depois um loop usando-os para criar uma cópia. Nota: aqui
!
não é usada sua avaliação indireta / dupla anterior, mas, no contexto da matriz, retorna os índices da matriz (chaves).descTable
eoptsTable
strings (sem[@]
), poderíamos usar o próprio array (como em referência) comeval
. para uma função genérica que aceita matrizes.fonte
Array1
, depois comArray2
, passar os nomes dos arrays se torne útil.O problema básico aqui é que os desenvolvedores do bash que projetaram / implementaram matrizes realmente estragaram tudo. Eles decidiram que
${array}
era apenas uma mão curta${array[0]}
, o que foi um grande erro. Especialmente quando você considera que${array[0]}
não tem significado e avalia a sequência vazia se o tipo de matriz é associativo.A atribuição de uma matriz assume a forma em
array=(value1 ... valueN)
que value tem a sintaxe[subscript]=string
, atribuindo um valor diretamente a um índice específico na matriz. Isso faz com que haja dois tipos de matrizes, indexadas numericamente e indexadas por hash (chamadas matrizes associativas na linguagem do bash). Ele também permite criar matrizes esparsas indexadas numericamente. Sair da[subscript]=
peça é uma abreviação para uma matriz indexada numericamente, começando com o índice ordinal de 0 e incrementando a cada novo valor na instrução de atribuição.Portanto,
${array}
deve avaliar para toda a matriz, índices e tudo. Deve avaliar o inverso da declaração de atribuição. Qualquer aluno do terceiro ano do ensino médio deve saber disso. Nesse caso, esse código funcionaria exatamente como você pode esperar:Em seguida, passar matrizes por valor para funções e atribuir uma matriz a outra funcionaria como o resto da sintaxe do shell exigir. Mas como eles não fizeram isso corretamente, o operador de atribuição
=
não funciona para matrizes, e matrizes não podem ser passadas por valor para funções ou subcascas ou saída em geral (echo ${array}
) sem código para analisar tudo.Portanto, se tivesse sido feito corretamente, o exemplo a seguir mostraria como a utilidade de matrizes no bash poderia ser substancialmente melhor:
a saída resultante deve ser:
Em seguida, as matrizes podem usar o operador de atribuição e serem passadas por valor para funções e até outros scripts de shell. Facilmente armazenado com saída para um arquivo e facilmente carregado de um arquivo para um script.
Infelizmente, fomos decepcionados por uma equipe superlativa de desenvolvimento do bash.
Como tal, para passar um array para uma função, existe realmente apenas uma opção, e é usar o recurso nameref:
resultará na seguinte saída:
Como isso é transmitido por referência, você também pode atribuir à matriz na função. Sim, a matriz que está sendo referenciada deve ter um escopo global, mas isso não deve ser muito importante, considerando que esse é um script de shell. Para passar uma matriz indexada associativa ou esparsa por valor para uma função, é necessário lançar todos os índices e valores na lista de argumentos (não muito útil se for uma matriz grande) como cadeias únicas como esta:
e, em seguida, escrevendo um monte de código dentro da função para remontar a matriz.
fonte
local -n
é melhor e mais atualizada do que a resposta aceita. Essa solução também funcionará para uma variável de qualquer tipo. O exemplo listado nesta resposta pode ser abreviado paralocal -n ARR=${1}
. No entanto, a-n
opçãolocal
/declare
está disponível apenas no Bash versão 4.3 e superior.funky ARR
), o shell emitirá um avisocircular name reference
, porque basicamente a função tentará executarlocal -n ARR=ARR
. Boa discussão sobre este tópico.A resposta do DevSolar tem um ponto que eu não entendo (talvez ele tenha um motivo específico para fazê-lo, mas não consigo pensar em um): ele define o array a partir dos parâmetros posicionais elemento por elemento, iterativo.
Uma abordagem mais fácil seria
fonte
Exemplo
fonte
Uma maneira fácil de passar várias matrizes como parâmetro é usar uma sequência separada por caracteres. Você pode chamar seu script assim:
Em seguida, você pode extraí-lo em seu código assim:
Dessa forma, você pode realmente passar várias matrizes como parâmetros e não precisa ser o último parâmetro.
fonte
Este funciona mesmo com espaços:
fonte
Com alguns truques, você pode realmente passar parâmetros nomeados para funções, juntamente com matrizes.
O método que desenvolvi permite acessar parâmetros passados para uma função como esta:
Em outras palavras, não apenas você pode chamar seus parâmetros pelos nomes (o que compõe um núcleo mais legível), como também pode passar matrizes (e referências a variáveis - esse recurso funciona apenas no bash 4.3)! Além disso, as variáveis mapeadas estão todas no escopo local, assim como $ 1 (e outras).
O código que faz esse trabalho é bastante leve e funciona tanto no bash 3 quanto no bash 4 (essas são as únicas versões com as quais eu testei). Se você estiver interessado em mais truques como esse que tornam o desenvolvimento com o bash muito mais agradável e fácil, você pode dar uma olhada no meu Bash Infinity Framework , o código abaixo foi desenvolvido para esse fim.
fonte
Apenas para adicionar à resposta aceita, como eu achei que não funcionaria bem se o conteúdo da matriz fosse algo como:
Nesse caso, cada membro da matriz é dividido, portanto, a matriz que a função vê é equivalente a:
Para que esse caso funcione, a maneira que encontrei é passar o nome da variável para a função e usar eval:
Apenas meu 2 ©
fonte
Por mais feia que seja, aqui está uma solução alternativa que funciona desde que você não passe uma matriz explicitamente, mas uma variável correspondente a uma matriz:
Tenho certeza de que alguém pode apresentar uma implementação mais clara da ideia, mas achei que essa é uma solução melhor do que passar uma matriz
"{array[@]"}
e acessá-la internamente usandoarray_inside=("$@")
. Isso se torna complicado quando existem outrosgetopts
parâmetros posicionais . Nesses casos, tive que primeiro determinar e remover os parâmetros não associados à matriz usando alguma combinação deshift
remoção e elemento da matriz.Uma perspectiva purista provavelmente vê essa abordagem como uma violação da linguagem, mas, pragmaticamente falando, essa abordagem me salvou bastante. Em um tópico relacionado, eu também uso
eval
para atribuir uma matriz construída internamente a uma variável nomeada de acordo com um parâmetro quetarget_varname
eu passo para a função:eval $target_varname=$"(${array_inside[@]})"
Espero que isso ajude alguém.
fonte
Requisito : Função para encontrar uma sequência em uma matriz.
Essa é uma ligeira simplificação da solução do DevSolar, na medida em que usa os argumentos passados em vez de copiá-los.
fonte
Minha resposta curta é:
${test_array[*]}
e${test_array2[*]}
deve estar cercado por "", caso contrário você falhará.fonte