Passe o array associativo como lista de parâmetros para o script

9

Em um script, tenho uma matriz associativa como:

declare -A VARS=( ["key1"]="value1" ["key2"]="value" )

Existe um único comando para transformar isso em uma lista de parâmetros no formulário

--key1=value1 --key2=value2

sem ter que reescrever manualmente

 --key1="${VARS[key1]}" --key2="${VARS[key2]}"

o caso de uso que eu tinha em mente era passar a matriz para um script como uma lista de parâmetros, como

my_script.sh $(to_param_list $VARS)

Para expandir o comentário que fiz na resposta do @Kusalananda, meu caso de uso exato é o seguinte: Eu tenho um script que é usado para criar um instalador de extração automática usando o makeelf, e esse script recebe alguns parâmetros que devem ser separados entre:

  • parâmetros para o próprio script
  • parâmetros para o instalador dentro do instalador de extração automática

Os scripts criam o instalador da seguinte maneira:

to_param_list installer_param_list installer_param_array
./makeself ./path/to/sourcedir ./path/to/created/installer "My installer" ./path/to/install/inside/package "${installer_param_list[@]}"

No entanto, testei o parâmetro passando com um script de instalação muito simples dentro do pacote:

while ! -z "$1" ; do
    echo "$1"
    shift
done

e passando uma matriz como:

installer_param_array=( ["upgrade-from"]="19 .2.0" ["upgrade-to"]="19.3.0" )

resulta nesta saída:

--upgrade-to=19.3.0
--upgrade-from=19
.2.0
Matteo Tassinari
fonte
Ele não responde à pergunta, mas outra maneira (no bash, uma das tags) é my_script.sh "$(declare -p thearray)$". Em myscript.sh você lê com source /dev/stdin <<<"$1"Então você tem thearrayem seu script. Você pode ter outros argumentos ao lado da matriz. Você pode passar muitas variáveis: my_script.sh "$(declare -p var1 var2 ...)"neste único argumento.
Dominic108

Respostas:

13

Com uma função auxiliar:

#!/bin/bash

to_param_list () {
    declare -n outlist=$1
    declare -n inhash=$2

    for param in "${!inhash[@]}"; do
        outlist+=( "--$param=${inhash[$param]}" )
    done
}

declare -A my_vars=( ["key1"]="value1" ["key2"]="value" )

to_param_list list my_vars
my_script.sh "${list[@]}"

O comando final no script acima seria expandido para o equivalente a ter escrito

my_script.sh "--key2=value" "--key1=value1"

A to_param_listfunção pega o nome de uma variável de matriz e o nome de uma variável de matriz associativa e os utiliza para criar duas variáveis ​​de "referência de nome" na função (os namerefs foram introduzidos na bashliberação 4.3). Eles são usados ​​para preencher a variável de matriz fornecida com as chaves e os valores no formato apropriado da matriz associativa.

O loop na função repete "${!inhash[@]}", que é a lista de chaves citadas individualmente em sua matriz associativa.

Quando a chamada de função retornar, o script usaria a matriz para chamar seu outro script ou comando.

Executando o acima com

declare -A my_vars=( ["key1"]="hello world" ["key2"]="some thing" ["key3"]="* * *" )

to_param_list list my_vars
printf 'Arg: %s\n' "${list[@]}"

o script sairia

Arg: --key2=some thing
Arg: --key3=* * *
Arg: --key1=hello world

Isso mostra que as opções são geradas sem que a divisão de palavras ou o globbing do nome do arquivo entrem em vigor. Também mostra que a ordem das chaves pode não ser preservada, pois o acesso às chaves a partir de uma matriz associativa o fará em uma ordem bastante aleatória.


Você não pode realmente usar uma substituição de comando com segurança aqui, pois o resultado seria uma única string. Se não estiver entre aspas, essa sequência será dividida em caracteres de espaço em branco (por padrão), que dividiriam adicionalmente as chaves e os valores de sua matriz associativa. O shell também executaria um globbing de nome de arquivo nas palavras resultantes. Citar duas vezes a substituição de comando não ajudaria, pois isso resultaria em chamar seu my_script.shcom um único argumento.


Em relação ao seu problema commakeself :

O makeselfscript faz isso com os argumentos do script do instalador:

SCRIPTARGS="$*"

Isso salva os argumentos como uma sequência $SCRIPTARGS(concatenada, separada por espaços). Posteriormente, ele é inserido no arquivo de extração automática como está. Para que as opções sejam analisadas corretamente quando são reavaliadas (que são ao executar o instalador), você precisará fornecer um conjunto extra de cotações nos valores dos parâmetros para que sejam delimitados corretamente.

installer_param_array=( ["upgrade-from"]="'19 .2.0'" ["upgrade-to"]="'19.3.0'" )

Observe que isso não é um bug no meu código. É apenas um efeito colateral da makeselfprodução de código shell com base nos valores fornecidos pelo usuário.

Idealmente, o makeselfscript deve ter escrito cada um dos argumentos fornecidos com um conjunto extra de aspas, mas não é presumível, porque é difícil saber qual efeito isso pode ter. Em vez disso, deixa ao usuário fornecer essas cotações extras.

Reexecutando meu teste de cima, mas agora com

declare -A my_vars=( ["key1"]="'hello world'" ["key2"]="'some value'" ["key3"]="'* * *'" )

to_param_list list my_vars
printf 'Arg: %s\n' "${list[@]}"

produz

Arg: --key2='some value'
Arg: --key3='* * *'
Arg: --key1='hello world'

Você pode ver que essas cadeias, quando reavaliadas pelo shell, não seriam divididas em espaços.

Obviamente, você pode usar sua matriz associativa inicial e adicionar as aspas na to_param_listfunção alterando

outlist+=( "--$param=${inhash[$param]}" )

para dentro

outlist+=( "--$param='${inhash[$param]}'" )

Uma dessas alterações no seu código incluiria aspas simples nos valores das opções, portanto, uma reavaliação dos valores seria necessária .

Kusalananda
fonte
Eu tentei sua solução, no entanto, se um dos valores na matriz associativa começando contém qualquer espaço, o comando resultante vai quebrar
Matteo Tassinari
@MatteoTassinari Não, não seria. Se isso acontecer, você esqueceu as aspas duplas em "--$param=${inhash[$param]}"ou dentro "${list[@]}", ou o script que recebe as opções faz algo errado ao analisá-las.
Kusalananda
Posso confirmar que usei as aspas como você está mostrando, esses parâmetros são passados ​​para github.com/megastep/makeself para criar um instalador que, quando executado, invoca um script com os parâmetros especificados, talvez nesta passagem algo dê errado
Matteo Tassinari
11
Não, não, tentarei editar minha pergunta para mostrar melhor meu caso de uso.
Matteo Tassinari
11
Eu adicionei meu caso de uso real e o erro que estou tendo #
Matteo Tassinari