「var name」を関数に渡し、関数が「var name」を使用して変数に含まれる値を変換し、変換されたオブジェクトを元の「var name」として参照できるようにします。
たとえば、区切りリストを配列に変換する関数があり、「animal_list」という区切りリストがあるとします。リスト名を関数に渡し、現在の配列を「animal_list」として参照して、リストを配列に変換したいと思います。
コード例:
function delim_to_array() {
local list=$1
local delim=$2
local oifs=$IFS;
IFS="$delim";
temp_array=($list);
IFS=$oifs;
# Now I have the list converted to an array but it's
# named temp_array. I want to reference it by its
# original name.
}
# ----------------------------------------------------
animal_list="anaconda, bison, cougar, dingo"
delim_to_array ${animal_list} ","
# After this point I want to be able to deal with animal_name as an array.
for animal in "${animal_list[@]}"; do
echo "NAME: $animal"
done
# And reuse this in several places to converted lists to arrays
people_list="alvin|baron|caleb|doug"
delim_to_array ${people_list} "|"
# Now I want to treat animal_name as an array
for person in "${people_list[@]}"; do
echo "NAME: $person"
done
ベストアンサー1
説明する
これを理解するには少し努力が必要です。忍耐を持ってください。このソリューションはbashでうまく機能します。 「バシム」が必要です。
まず、変数への「間接」アクセスを使用する必要があります${!variable}
。$variable
文字列が含まれている場合は、animal_name
「パラメータ拡張」:${!variable}
に展開されます$animal_name
。
このアイデアが実際に実行されている様子を見てみましょう。わかりやすくするために、使用した名前と値を最大限に保ちました。
#!/bin/bash
function delim_to_array() {
local VarName=$1
local IFS="$2";
printf "inside IFS=<%s>\n" "$IFS"
echo "inside var $VarName"
echo "inside list = ${!VarName}"
echo a\=\(${!VarName}\)
eval a\=\(${!VarName}\)
printf "in <%s> " "${a[@]}"; echo
eval $VarName\=\(${!VarName}\)
}
animal_list="anaconda, bison, cougar, dingo"
delim_to_array "animal_list" ","
printf "out <%s> " "${animal_list[@]}"; echo
printf "outside IFS=<%s>\n" "$IFS"
# Now we can use animal_name as an array
for animal in "${animal_list[@]}"; do
echo "NAME: $animal"
done
フルスクリプトを実行すると(so-setvar.shと仮定)、次のようになります。
$ ./so-setvar.sh
inside IFS=<,>
inside var animal_list
inside list = anaconda, bison, cougar, dingo
a=(anaconda bison cougar dingo)
in <anaconda> in <bison> in <cougar> in <dingo>
out <anaconda> out <bison> out <cougar> out <dingo>
outside IFS=<
>
NAME: anaconda
NAME: bison
NAME: cougar
NAME: dingo
「内部」は「関数の内部」を意味し、「外部」はその逆を意味することを理解してください。
内部値は文字列の$VarName
var:の名前です。animal_list
値は${!VarName}
リストとして表示されます。anaconda, bison, cougar, dingo
これで、ソリューションがどのように構築されたかを示すために、echoを含む行があります。
echo a\=\(${!VarName}\)
eval
次の行が何をするかを示します。
a=(anaconda bison cougar dingo)
一度評価するはい、変数はa
動物のリストを含む配列です。この場合、var a は、eval がどのように影響するかを正確に示すために使用されます。
その後、各要素の値がa
印刷されます<in> val
。そして、次の2行に示す
ように、関数の外側の部分でも同じことを行います。<out> val
in <anaconda> in <bison> in <cougar> in <dingo>
out <anaconda> out <bison> out <cougar> out <dingo>
実際の変更は、関数の最後の評価で行われます。
それはすべてです。 varには現在値配列があります。
実際、この関数の中心は1行です。eval $VarName\=\(${!VarName}\)
さらに、IFSの値は関数のローカル値に設定されているため、追加の操作なしで関数が実行される前の値に戻ることができます。ありがとうピーター・コルデス独創的なアイデアについての意見です。
これで説明が終わりました。明確になりますように。
実際の機能
不要な行をすべて削除し、コア評価のみを残し、IFSの新しい変数のみを作成すると、関数を最小限の表現に減らします。
delim_to_array() {
local IFS="${2:-$' :|'}"
eval $1\=\(${!1}\);
}
IFS値をローカル変数に設定すると、関数の「デフォルト」値を設定することもできます。 IFSが要求する値が2番目のパラメーターとして関数に送信されない限り、ローカルIFSは「デフォルト」値を想定します。デフォルトはspace()(常に有用な分割値です)、colon(:)、vertical line(|)でなければならないと思います。これら3つのうちの1つは値を分割します。もちろん、デフォルト値は必要に応じて異なる値に設定できます。
使用するように編集read
:
evalで参照されていない値のリスクを減らすために、次のものを使用できます。
delim_to_array() {
local IFS="${2:-$' :|'}"
# eval $1\=\(${!1}\);
read -ra "$1" <<<"${!1}"
}
test="fail-test"; a="fail-test"
animal_list='bison, a space, {1..3},~/,${a},$a,$((2+2)),$(echo "fail"),./*,*,*'
delim_to_array "animal_list" ","
printf "<%s>" "${animal_list[@]}"; echo
$ so-setvar.sh
<bison>< a space>< {1..3}><~/><${a}><$a><$((2+2))><$(echo "fail")><./*><*><*>
上記のvarに設定されたほとんどの値はanimal_list
evalで失敗します。
しかし、問題なく読んでください。
- 注:evalオプションを試してみるのは完全に安全です。このコードではこれは、関数を呼び出す前に変数値がプレーンテキスト値に設定されているためです。実際に実行されても、それは単なるテキストです。パス名拡張が最後の拡張であるため、ファイル名が間違っても問題は発生しないため、パス名拡張では変数拡張は再実行されません。また、現状のコードこれは決して汎用的な検証ではありません
eval
。
はい
この関数の機能と仕組みを実際に理解するために、この関数を使用して公開したコードを書き直しました。
#!/bin/bash
delim_to_array() {
local IFS="${2:-$' :|'}"
# printf "inside IFS=<%s>\n" "$IFS"
# eval $1\=\(${!1}\);
read -ra "$1" <<<"${!1}";
}
animal_list="anaconda, bison, cougar, dingo"
delim_to_array "animal_list" ","
printf "NAME: %s\t " "${animal_list[@]}"; echo
people_list="alvin|baron|caleb|doug"
delim_to_array "people_list"
printf "NAME: %s\t " "${people_list[@]}"; echo
$ ./so-setvar.sh
NAME: anaconda NAME: bison NAME: cougar NAME: dingo
NAME: alvin NAME: baron NAME: caleb NAME: doug
ご覧のとおり、IFSは関数内でのみ設定され、永久に変更されないため、以前の値にリセットする必要はありません。さらに、「people_list」関数への2番目の呼び出しは、2番目のパラメータを設定せずにIFSのデフォルト値を利用します。
警告01:
(eval)関数を設定すると、varが引用符なしでシェル解析に公開される点があります。これにより、IFS値を使用して「単語の分割」を完了できます。しかし、これはまた、varの値を「中括弧拡張」、「チルダ拡張」、「引数、変数および算術拡張」、「コマンド置換」、および「パス名拡張」に公開します。ここでコマンドは次のようになります。<() >()
これをサポートするシステムでプロセス交換を実行します。
すべての例(最後の例を除く)は、次の単純なエコーでラップされます(注意してください)。
a=failed; echo {1..3} ~/ ${a} $a $((2+2)) $(ls) ./*
つまり、ファイル{~$`<>
名で始まる、ファイル名と一致する、またはファイル名を含めることができる文字列は、潜在的な問題になる?*[]
可能性があります。
変数にそのような問題のある値が含まれていないと確信している場合は安全です。そのような値を持つことが可能であれば、あなたの質問に答える方法はより複雑で、より多くの(またはより長い)説明と説明が必要です。使用はread
代替品です。
警告02:
はい、独自のread
「ドラゴン」があります。
- 常に-rオプションを使用してください。このオプションが不要な状況は考えられません。
- この
read
コマンドは1行だけインポートできます。オプションを設定しても複数行を使用するには-d
特別な注意が必要です。または、入力全体を変数に割り当てます。 - 値にスペースが含まれている場合は、先行スペースと末尾の
IFS
スペースが削除されます。まあ、完全な説明にはそれに関するいくつかの詳細を含める必要がありますtab
が、省略します。 - パイプを介して
|
データを読み取らないでください。これにより、サブシェルから読み取りが行われます。子シェルに設定されたすべての変数は、親シェルに戻っても保持されません。まあ、いくつかの回避策がありますが、再び詳細はスキップします。
もともと読書に関する警告や問題を含めたくはありませんでしたが、一般のニーズに合わせて含める必要がありました。申し訳ありません。