web-dev-qa-db-ja.com

置換$ {!var_name + x}はどういう意味ですか?

変数が設定されているかどうかをチェックする機能を持つスクリプトを見つけましたが、よくわかりません。

check_if_variable_is_set() {
    var_name=$1
    if [ -z "${!var_name+x}" ]; then
        false
    else
        true
    fi
}

この置換で正確に何が起こりますか?

10
Karim Manaouil

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:]]_は、ロケールでアルファベット文字をチェックしますが、一部のシェルは、変数にポータブル文字セットのアルファベット文字のみを受け入れることに注意してください)

17
Kusalananda