何をすべきか?

何をすべきか?

私はいくつかの異なるパラメータを使用してコマンド(疑問がある場合はweka)を数回呼び出すスクリプトを書いています。パラメータの1つを'-search "< stuff >" '引用する必要があります。場合によっては、これらのパラメータを複数使用する必要があります。

weka a -search "a params" -search "other a params"

weka b -search "just these params"`

私は次の連想配列を使用しようとしてきました。

search=( ["a"]='-search "a params" -search "other a params"'
["b"]='-search "just these params"'

それから電話をかけます。

function call_thing_with_the_right_parameters_so_it_works_out_alright {
    weka $1 ${search["$1"]}
} # <-- closing brace for the function
call_thing_with_the_right_parameters_so_it_works_out_alright

ああ、私が何をしようとしても、引用が混乱しました。

bash -x ./this_is_the_name_of_my_script_sorry_its_so_long
...
+ weka a -search '"a' 'params"' # <-- (not what I want)
...

どんなアイデアがありますか?

答え1

コード行はweka $1 ${search["$1"]}シェル分割の影響を受けます。
変数を変更しないと、分割が$IFS発生しますspacetabnew line

行は次に展開されます。

weka $1 ${search["$1"]}
weka a -search "a params" -search "other a params"

しかし、上記のように分割すると、次のような意味になります。

<weka> <a> <-search> <"a> <params"> <-search> <"other> <a> <params">

行の前にまったく同じprintfが追加されていることがわかります。

$ printf '<%s> ' weka $1 ${search["$1"]}; echo
<weka> <a> <-search> <"a> <params"> <-search> <"other> <a> <params">

変数が正しく引用されると、状況はより良くなります。

$ printf '<%s> ' weka "$1" "${search["$1"]}"; echo
<weka> <a> <-search "a params" -search "other a params">

しかし、それはあなたが望むものではありません。分割する必要がありますが、単にスペースだけを分割しないでください。

何をすべきか?

2つの解決策があります。

手動分割

一部の文字を使用して#分割位置を手動で表示します。

search=( ["a"]='-search#a params#-search#other a params' ["b"]='-search#just these params' )

IFS次に、文字列を新しい配列に分割できるように、分割に使用した文字を bash に伝えますb

IFS='#'
b=( ${search["a"]} )
printf '<%s> ' "${b[@]}"; echo

次を生成します。

<-search> <a params> <-search> <other a params> 

希望の正確な分割。
唯一の問題はIFS変更されたことですが、ローカル関数でこの問題を解決できますIFS

callsplit(){
    local IFS='#'
    b=( ${search["$1"]} )
    weka "$1" "${b[@]}"
}

使用評価

別の解決策は、シェルが行を分割する一般的な方法と同様に、シェルがコマンドラインを分割できるように、evalを使用してコマンドラインを再解析することです。
変数検索の値は定義したとおりです。

search=( ["a"]='-search "a params" -search "other a params"'  
         ["b"]='-search "just these params"' )

しかし、evalを使用して実行行を拡張します。

eval weka "$1" "${search["$1"]}"

行がどのように拡張されるかを確認するには、次のコマンドを使用します。

$ eval printf "'<%s> '" weka "$1" "${search["$1"]}"; echo
<weka> <a> <-search> <a params> <-search> <other a params>

完全なスクリプトは次のとおりです。

#!/bin/bash
declare -A search
search+=( ["a"]='-search "a params" -search "other a params"')
search+=( ["b"]='-search "just these params"' )

call_thing() {
    eval weka "$1" "${search["$1"]}"
} # <-- closing brace for the function

call_thing "a"

注:検索値が設定されていると仮定すると正常に動作します。~へスクリプト(外部攻撃者は設定できません)と値は、通常のシェル「コマンドライン」と呼ばれます。

警告する:evalを使用すると、文字列形式のデータをコマンドに似たコードに変換できます。この特定のスクリプトでは、次の行は次のようになります。

call_thing "a; touch new_file"

コマンドが実行されますtouch new_file。ただし、他のコマンドも実行できます。供給に注意し、非常に注意してくださいeval

上記のように、シェルで実行できる危険なコマンドがたくさんあることに注意してくださいrm -r /。このコマンドはevalどのコマンドよりも強力ではありません。注意してください。

答え2

シェルは目的の方法で引用符を処理しません。 (引用符を一度引用すると、通常の文字として扱われます。)

bashをだまして必要な操作を実行できます。連想配列定義を開始するには、次のようにします。

declare -A s
s=( ["a"]='-search;a params;-search;other a params;' ["b"]='-search;just these params' )

ご覧のとおり、文字列にはセミコロンで区切られた各コマンドの引数が含まれています。たとえば、の引数を抽出するには、これをフィールド区切り文字aとして使用するようにシェルに指示します。;

IFS=\; v=(${s[a]})

vこれで、配列に次の必須フィールドがあることを確認できます。

$ declare -p v
declare -a v='([0]="-search" [1]="a params" [2]="-search" [3]="other a params")'

その後、次のように実行できますweka

weka a "${v[@]}"

したがって、すべてをまとめると、関数定義は次のようになります。

declare -A s
s=( ["a"]='-search;a params;-search;other a params;' ["b"]='-search;just these params' )
callthing() (
        IFS=\; v=(${s[$1]})
        weka "$1" "${v[@]}"
)

関連情報