Pass array asosiatif sebagai daftar parameter ke skrip

9

Dalam skrip saya memiliki array asosiatif seperti:

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

Apakah ada satu perintah untuk mengubah itu menjadi daftar parameter dalam formulir

--key1=value1 --key2=value2

tanpa harus menulis ulang secara manual

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

use case yang ada dalam pikiran saya adalah untuk melewatkan array ke skrip sebagai daftar parameter, seperti

my_script.sh $(to_param_list $VARS)

Untuk memperluas komentar yang saya buat di @Kusalananda jawaban, kasus penggunaan tepat saya adalah sebagai berikut: Saya punya skrip yang digunakan untuk membangun installer self-extracting menggunakan makeelf, dan skrip ini menerima beberapa parameter yang harus dipisahkan antara:

  • parameter untuk skrip itu sendiri
  • parameter untuk installer di dalam installer self-extracting

Script kemudian membangun installer seperti ini:

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

Namun, saya telah menguji parameter yang lewat dengan skrip installer yang sangat sederhana di dalam paket:

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

dan melewatkan array seperti:

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

hasil dalam output ini:

--upgrade-to=19.3.0
--upgrade-from=19
.2.0
Matteo Tassinari
sumber
Itu tidak menjawab pertanyaan, tetapi cara lain (dalam bash, salah satu tag) adalah my_script.sh "$(declare -p thearray)$". Di myscript.sh Anda membacanya dengan source /dev/stdin <<<"$1"Lalu Anda miliki thearraydi skrip Anda. Anda dapat memiliki argumen lain di samping array. Anda dapat melewati banyak variabel: my_script.sh "$(declare -p var1 var2 ...)"dalam argumen tunggal ini.
Dominic108

Jawaban:

13

Dengan fungsi pembantu:

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

Perintah terakhir dalam skrip di atas akan diperluas menjadi setara dengan yang telah ditulis

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

The to_param_listFungsi mengambil nama dari variabel array dan nama dari variabel array asosiatif dan menggunakan ini untuk membuat dua "referensi nama" variabel dalam fungsi (namerefs diperkenalkan pada bashrilis 4.3). Ini kemudian digunakan untuk mengisi variabel array yang diberikan dengan kunci dan nilai dalam format yang sesuai dari array asosiatif.

Pengulangan dalam fungsi berulang "${!inhash[@]}", yang merupakan daftar kunci yang dikutip secara individual dalam array asosiatif Anda.

Setelah pemanggilan fungsi kembali, skrip akan menggunakan array untuk memanggil skrip atau perintah Anda yang lain.

Menjalankan di atas dengan

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

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

script akan ditampilkan

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

Ini menunjukkan bahwa opsi dihasilkan tanpa pemisahan kata atau penggumpalan nama file mulai berlaku. Ini juga menunjukkan bahwa urutan kunci tidak dapat dipertahankan karena mengakses kunci dari array asosiatif akan melakukannya dalam urutan yang cukup acak.


Anda tidak dapat benar-benar menggunakan substitusi perintah dengan aman di sini karena itu akan menjadi string tunggal. Jika tidak dikutip, string ini kemudian akan dipisah pada karakter spasi putih (secara default), yang juga akan membagi kunci dan nilai array asosiatif Anda. Shell juga akan melakukan nama file globbing pada kata-kata yang dihasilkan. Mengutip dua kali substitusi perintah tidak akan membantu karena itu akan menghasilkan panggilan Anda my_script.shdengan satu argumen.


Mengenai masalah Anda denganmakeself :

The makeselfScript melakukan ini dengan argumen untuk installer script Anda:

SCRIPTARGS="$*"

Ini menyimpan argumen sebagai string dalam $SCRIPTARGS(disatukan, dipisahkan oleh spasi). Ini kemudian dimasukkan ke dalam arsip self-extracting apa adanya. Agar opsi diuraikan dengan benar ketika mereka dievaluasi kembali (yang mana ketika menjalankan installer), Anda harus memberikan seperangkat tanda kutip tambahan dalam nilai-nilai parameter agar mereka dibatasi secara tepat.

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

Perhatikan bahwa ini bukan bug dalam kode saya. Ini hanya efek samping dari makeselfpembuatan kode shell berdasarkan nilai yang diberikan pengguna.

Idealnya, makeselfskrip seharusnya menulis masing-masing argumen yang disediakan dengan seperangkat tanda kutip tambahan di sekitar mereka, tetapi tidak, mungkin karena sulit untuk mengetahui efek apa yang mungkin terjadi. Sebagai gantinya, ia menyerahkannya kepada pengguna untuk memberikan penawaran tambahan ini.

Menjalankan kembali pengujian saya dari atas, tetapi sekarang dengan

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

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

menghasilkan

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

Anda dapat melihat bahwa string ini , ketika dievaluasi kembali oleh shell, tidak akan terpecah pada spasi.

Jelas, Anda bisa menggunakan array asosiatif awal Anda dan sebagai gantinya menambahkan kutipan dalam to_param_listfungsi dengan mengubah

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

ke

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

Salah satu dari perubahan ini pada kode Anda akan menyertakan tanda kutip tunggal dalam nilai-nilai opsi, sehingga evaluasi ulang dari nilai-nilai akan menjadi perlu .

Kusalananda
sumber
Saya mencoba solusi Anda, namun jika salah satu nilai dalam array asosiatif awal berisi ruang apa pun, perintah yang dihasilkan akan terputus
Matteo Tassinari
@MatteoTassinari Tidak, itu tidak akan. Jika ya, maka Anda telah melupakan tanda kutip ganda di "--$param=${inhash[$param]}"atau di dalam "${list[@]}", atau skrip yang menerima opsi melakukan kesalahan dalam menguraikannya.
Kusalananda
Saya dapat mengonfirmasi bahwa saya menggunakan tanda kutip seperti yang Anda tunjukkan, parameter ini dilewatkan ke github.com/megastep/makeself untuk membuat installer yang, ketika dijalankan, memanggil skrip dengan parameter yang diberikan, mungkin dalam bagian ini ada yang tidak beres
Matteo Tassinari
1
Tidak, bukan itu, saya akan mencoba mengedit pertanyaan saya untuk lebih menunjukkan kasus penggunaan saya.
Matteo Tassinari
1
Saya menambahkan kasus penggunaan saya yang sebenarnya dan kesalahan yang saya alami
Matteo Tassinari