変数が設定されているかどうかをチェックする機能を持つスクリプトを見つけましたが、よくわかりません。
check_if_variable_is_set() {
var_name=$1
if [ -z "${!var_name+x}" ]; then
false
else
true
fi
}
この置換で正確に何が起こりますか?
bash
シェルでは、_${!var}
_は変数の間接参照です。これは、名前が_$var
_に保持されている変数の値に展開されます。
変数展開_${var+value}
_は POSIX展開 で、変数value
が設定されている場合はvar
に展開されます(値が空かどうかに関係なく) 。
これらを組み合わせると、_${!var+x}
_に名前が保持されている変数が設定されている場合、_$var
_はx
に展開されます。
例:
_$ foo=hello
$ var=foo
_
_$ echo "${!var+$var is set, its value is ${!var}}"
foo is set, its value is hello
_
_$ unset foo
$ echo "${!var+$var is set, its value is ${!var}}"
_
(出力として空の行)
問題の関数は、次のように短縮できます。
_check_if_variable_is_set () { [ -n "${!1+x}" ]; }
_
あるいは:
_check_if_variable_is_set () { [ -v "$1" ]; }
_
あるいは:
_check_if_variable_is_set()[[ -v $1 ]]
_
ここで、_-v
_は、変数名のbash
テストであり、名前付き変数が設定されている場合はtrue、それ以外の場合はfalseになります。
POSIXly、それは書くことができます:
_check_if_variable_is_set() { eval '[ -n "${'"$1"'+x}" ]'; }
_
その関数への引数が攻撃者の制御下に置かれる可能性がある場合、これらすべてが潜在的なコマンドインジェクションの脆弱性であることに注意してください。たとえば、check_if_variable_is_set 'a[$(id>&2)]'
で試してください。
これを防ぐには、最初に引数が有効な変数名であることを確認する必要があります。変数の場合:
_check_if_variable_is_set() {
case $1 in
("" | *[![:alnum:]_]* | [![:alpha:]_]*) false;;
(*) eval '[ -n "${'"$1"'+x}" ]'
esac
}
_
(_[[:alpha:]]
_は、ロケールでアルファベット文字をチェックしますが、一部のシェルは、変数にポータブル文字セットのアルファベット文字のみを受け入れることに注意してください)