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

Kar*_*uil 10 bash shell-script variable-substitution

我找到了一个脚本,它有一个函数来检查是否设置了变量,但我不太明白。

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

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

Kus*_*nda 17

bashshell中,${!var}是一个变量间接。它扩展为名称保存在$var.

变量扩展${var+value}一个 POSIX 扩展value如果变量var被设置则扩展为(无论其值是否为空)。

结合这些,如果设置了名称保留的变量,${!var+x}则将扩展为。x$var

例子:

$ foo=hello
$ var=foo
Run Code Online (Sandbox Code Playgroud)
$ echo "${!var+$var is set, its value is ${!var}}"
foo is set, its value is hello
Run Code Online (Sandbox Code Playgroud)
$ unset foo
$ echo "${!var+$var is set, its value is ${!var}}"
Run Code Online (Sandbox Code Playgroud)

(空行作为输出)


问题中的功能可以缩短为

check_if_variable_is_set () { [ -n "${!1+x}" ]; }
Run Code Online (Sandbox Code Playgroud)

甚至:

check_if_variable_is_set () { [ -v "$1" ]; }
Run Code Online (Sandbox Code Playgroud)

甚至:

check_if_variable_is_set()[[ -v $1 ]]
Run Code Online (Sandbox Code Playgroud)

哪里-vbash对变量名称的测试,如果设置了命名变量,则为真,否则为假。


POSIXly,它可以写成:

check_if_variable_is_set() { eval '[ -n "${'"$1"'+x}" ]'; }
Run Code Online (Sandbox Code Playgroud)

请注意,如果该函数的参数最终可能受到攻击者的控制,那么所有这些都是潜在的命令注入漏洞。尝试例如使用check_if_variable_is_set 'a[$(id>&2)]'.

为了防止这种情况,您可能需要先验证参数是否是有效的变量名。对于变量:

check_if_variable_is_set() {
  case $1 in
    ("" | *[![:alnum:]_]* | [![:alpha:]_]*) false;;
    (*)  eval '[ -n "${'"$1"'+x}" ]'
  esac
}
Run Code Online (Sandbox Code Playgroud)

(请注意,[[:alpha:]]将检查您的语言环境中的字母字符而某些 shell 仅接受来自其变量中可移植字符集的字母字符)