web-dev-qa-db-ja.com

連想配列をパラメーターリストとしてスクリプトに渡す

スクリプトには、次のような連想配列があります。

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

それをフォームのパラメータリストに変換する単一のコマンドはありますか

--key1=value1 --key2=value2

手動で書き直す必要なし

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

私が考えていたユースケースは、次のように、パラメータのリストとして配列をスクリプトに渡すことでした。

my_script.sh $(to_param_list $VARS)

@Kusalanandaの回答で行ったコメントをさらに詳しく説明すると、私の正確な使用例は次のとおりです。makeselfを使用して自己解凍インストーラーをビルドするために使用されるスクリプトがあり、このスクリプトは、次のように区切られるパラメーターを受け取ります。

  • スクリプト自体のパラメーター
  • 自己解凍インストーラー内のインストーラーのパラメーター

次に、スクリプトは次のようにインストーラーをビルドします。

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

ただし、パッケージ内の非常に単純なインストーラースクリプトを使用してパラメーターの受け渡しをテストしました。

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

次のような配列を渡します:

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

この出力結果:

--upgrade-to=19.3.0
--upgrade-from=19
.2.0
8

ヘルパー関数で:

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

上記のスクリプトの最後のコマンドは、

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

to_param_list関数は、配列変数のnameと連想配列変数のnameを取り、これらを使用して関数に2つの「名前参照」変数を作成します(namerefはbashリリース4.3で導入されました)。次に、これらを使用して、指定した配列変数に、連想配列から適切な形式のキーと値を入力します。

関数のループは"${!inhash[@]}"を反復します。これは、連想配列で個別に引用されたキーのリストです。

関数呼び出しが戻ると、スクリプトは配列を使用して他のスクリプトまたはコマンドを呼び出します。

上記を実行する

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

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

スクリプトは出力します

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

これは、Word分割やファイル名グロビングが有効にならないでオプションが生成されることを示しています。また、連想配列からキーにアクセスするとかなりランダムな順序でキーがアクセスされるため、キーの順序が保持されない可能性があることも示しています。


結果として単一の文字列になるため、ここでは実際にコマンド置換を安全に使用することはできません。引用符で囲まれていない場合、この文字列は空白文字(デフォルト)で分割され、連想配列のキーと値の両方がさらに分割されます。シェルは、結果の単語に対してファイル名のグロビングも実行します。コマンド置換を二重引用符で囲んでも、single引数を使用してmy_script.shが呼び出されるため、役に立ちません。


makeselfの問題について

makeselfスクリプトは、インストーラースクリプトへの引数を使用してこれを行います。

SCRIPTARGS="$*"

これにより、引数が$SCRIPTARGSで文字列として保存されます(連結され、スペースで区切られます)。これは後で自己解凍型アーカイブにそのまま挿入されます。オプションがre-evaluated(インストーラーの実行時)であるときに正しく解析されるようにするには、extraパラメータの値に含まれる引用符のセットを適切に区切る。

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

これは私のコードのバグではないことに注意してください。これは、ユーザーが指定した値に基づいてmakeselfが生成するシェルコードの副作用にすぎません。

理想的には、makeselfスクリプトは、提供された各引数を引用符で囲む必要がありますが、そうではない可能性があります。代わりに、これらの追加の引用を提供するのはユーザーに任されます。

上からテストを再実行しますが、

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

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

作り出す

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

シェルによってこれらの文字列がre-evaluatedされた場合、スペースで分割されないことがわかります。

明らかに、最初の連想配列を使用し、代わりにto_param_list関数に引用符を追加して、

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

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

コードへのこれらの変更のどちらでも、オプションの値に単一引用符が含まれるため、値の再評価はが必要になります

11
Kusalananda