替换 ${!var_name+x} 是什么意思?

替换 ${!var_name+x} 是什么意思?

我发现一个脚本有一个函数来检查变量是否已设置,但我不太理解它。

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

这个替换到底会发生什么?

答案1

bashshell中,${!var}是一个变量间接寻址。它扩展到名称保存在 中的变量的值$var

变量展开式${var+value}POSIX 扩展扩展为value如果设置了变量var(无论其值是否为空)。

将它们组合起来,${!var+x}将扩展为是否设置了x名称保存的变量。$var

例子:

$ 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 ]]

其中-vbash对变量名称的测试,如果设置了命名变量,则为 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:]]将检查字母字符在您所在的地区而某些 shell 仅接受其变量中可移植字符集中的字母字符)

相关内容