尝试递增关联数组元素时出现错误的数组下标错误
Bad array subscript error when trying to increment an associative array element
我可以创建一个 关联数组 并将一个整数分配给其中包含 单引号 的键:
$ declare -A dict
$ var="john's"
$ dict[$var]=1
$ echo ${dict[$var]}
1
$ declare -p dict
declare -A dict=(["john's"]="1" )
但是当我尝试增加它的值时:
$ (( dict[$var]++ ))
bash: ((: dict[john's]++ : bad array subscript (error token is "dict[john's]++ ")
$ (( dict["$var"]++ ))
bash: ((: dict[john's]++ : bad array subscript (error token is "dict[john's]++ ")
$ (( dict["${var}"]++ ))
bash: ((: dict[john's]++ : bad array subscript (error token is "dict[john's]++ ")
我总是得到同样的错误。我做错了什么?
key
名称中的单引号导致解析器将其视为未终止的引号字符。解决此问题的一种方法是转义键
中的 '
字符
key="john's"
printf -v escKey "%q" "$key"
现在由于 %q
说明符,printf()
将对所有 shell 元字符应用所需的转义,即使其 "shell-quoted" 并可重复使用。如果您打印 escKey
的内容,您会注意到 '
已转义
printf '%s\n' "$escKey"
john\'s
现在您可以在关联数组中使用这个键名了。请记住,您始终可以手动添加可能会很混乱的转义符。由于 %q
是 shell 提供的本地方式,因此使用它非常安全。
(( dict["$escKey"]++ ))
同样在 bash
版本 >= 4.4 parameter expansion 中有 @Q
这是 printf()
的 %q
说明符的快捷方式,你可以使用它
(( dict["${key@Q}"]++ ))
您需要通过转义 $
来确保 $var
在 Bash 开始将 dict[$var]++
解析为算术表达式之前不会展开。
$ (( dict[$var]++ ))
$ declare -p dict
declare -A dict=(["john's"]="2" )
在较新版本的 Bash 上,启用 assoc_expand_once
shell 选项也有效,但这是 不安全的 从不受信任的来源。 Stephane's answer.
中给出了一个示例,说明即使启用此选项,shell 仍然可以被欺骗以使用受污染的 $var
执行任意命令。
是的,不仅如此
(( dict[$var]++ ))
不适用于 $var
的某些值,但它也是一个 任意命令执行 漏洞。
bash-5.1$ var='1$(uname>&2)'
bash-5.1$ typeset -A dict
bash-5.1$ (( dict[$var]++ ))
Linux
Linux
用 printf %q
或 ${var@Q}
引用 $var
的值不是一种选择,因为对于 $var
的某些值,这些运算符使用 $'...'
在算术表达式中无法理解的引用形式:
bash-5.1$ typeset -A dict
bash-5.1$ var=$(seq 2)
bash-5.1$ (( dict[${var@Q}]++ ))
bash-5.1$ echo "${!dict[@]}"
\n2
bash-5.1$ var=$'\n\'$(uname >&2)],x['
bash-5.1$ (( dict["${var@Q}"]++ ))
Linux
bash: ((: dict[$'\n\'$(uname >&2)],x[']++ : bad array subscript (error token is "x[']++ ")
(仍然是 ACE)。
使用新的(自 5.0 起)assoc_expand_once
没有帮助,因为它仍然没有解决 @
/*
值或 ]
字符(因此不修复 ACE 漏洞):
bash-5.1$ shopt -s assoc_expand_once
bash-5.1$ var='x],b[1$(uname>&2)'
bash-5.1$ typeset -A dict
bash-5.1$ (( dict[$var]++ ))
Linux
bash
(不是 ksh93
也不是 zsh
)的另一个问题是 bash 关联数组不支持空键,所以你可以' t 使用任意字符串作为关联数组键。解决方法是 prefix/suffix 使用固定字符串的所有键。
在这里,你可以做:
dict[.$var]=$(( ${dict[.$var]} + 1 ))
或(假设assoc_expand_once
选项未启用):
let 'dict[.$var]++'
(确保使用 单 引号)。
这里使用.
作为前缀。
请参阅 How to use associative arrays safely inside arithmetic expressions? 了解所有细节和更多注意事项。
更一般地说,shell 算术表达式是一个雷区。另见:Security Implications of using unsanitized data in Shell Arithmetic evaluation.
我可以创建一个 关联数组 并将一个整数分配给其中包含 单引号 的键:
$ declare -A dict
$ var="john's"
$ dict[$var]=1
$ echo ${dict[$var]}
1
$ declare -p dict
declare -A dict=(["john's"]="1" )
但是当我尝试增加它的值时:
$ (( dict[$var]++ ))
bash: ((: dict[john's]++ : bad array subscript (error token is "dict[john's]++ ")
$ (( dict["$var"]++ ))
bash: ((: dict[john's]++ : bad array subscript (error token is "dict[john's]++ ")
$ (( dict["${var}"]++ ))
bash: ((: dict[john's]++ : bad array subscript (error token is "dict[john's]++ ")
我总是得到同样的错误。我做错了什么?
key
名称中的单引号导致解析器将其视为未终止的引号字符。解决此问题的一种方法是转义键
'
字符
key="john's"
printf -v escKey "%q" "$key"
现在由于 %q
说明符,printf()
将对所有 shell 元字符应用所需的转义,即使其 "shell-quoted" 并可重复使用。如果您打印 escKey
的内容,您会注意到 '
已转义
printf '%s\n' "$escKey"
john\'s
现在您可以在关联数组中使用这个键名了。请记住,您始终可以手动添加可能会很混乱的转义符。由于 %q
是 shell 提供的本地方式,因此使用它非常安全。
(( dict["$escKey"]++ ))
同样在 bash
版本 >= 4.4 parameter expansion 中有 @Q
这是 printf()
的 %q
说明符的快捷方式,你可以使用它
(( dict["${key@Q}"]++ ))
您需要通过转义 $
来确保 $var
在 Bash 开始将 dict[$var]++
解析为算术表达式之前不会展开。
$ (( dict[$var]++ ))
$ declare -p dict
declare -A dict=(["john's"]="2" )
在较新版本的 Bash 上,启用 assoc_expand_once
shell 选项也有效,但这是 不安全的 从不受信任的来源。 Stephane's answer.
$var
执行任意命令。
是的,不仅如此
(( dict[$var]++ ))
不适用于 $var
的某些值,但它也是一个 任意命令执行 漏洞。
bash-5.1$ var='1$(uname>&2)'
bash-5.1$ typeset -A dict
bash-5.1$ (( dict[$var]++ ))
Linux
Linux
用 printf %q
或 ${var@Q}
引用 $var
的值不是一种选择,因为对于 $var
的某些值,这些运算符使用 $'...'
在算术表达式中无法理解的引用形式:
bash-5.1$ typeset -A dict
bash-5.1$ var=$(seq 2)
bash-5.1$ (( dict[${var@Q}]++ ))
bash-5.1$ echo "${!dict[@]}"
\n2
bash-5.1$ var=$'\n\'$(uname >&2)],x['
bash-5.1$ (( dict["${var@Q}"]++ ))
Linux
bash: ((: dict[$'\n\'$(uname >&2)],x[']++ : bad array subscript (error token is "x[']++ ")
(仍然是 ACE)。
使用新的(自 5.0 起)assoc_expand_once
没有帮助,因为它仍然没有解决 @
/*
值或 ]
字符(因此不修复 ACE 漏洞):
bash-5.1$ shopt -s assoc_expand_once
bash-5.1$ var='x],b[1$(uname>&2)'
bash-5.1$ typeset -A dict
bash-5.1$ (( dict[$var]++ ))
Linux
bash
(不是 ksh93
也不是 zsh
)的另一个问题是 bash 关联数组不支持空键,所以你可以' t 使用任意字符串作为关联数组键。解决方法是 prefix/suffix 使用固定字符串的所有键。
在这里,你可以做:
dict[.$var]=$(( ${dict[.$var]} + 1 ))
或(假设assoc_expand_once
选项未启用):
let 'dict[.$var]++'
(确保使用 单 引号)。
这里使用.
作为前缀。
请参阅 How to use associative arrays safely inside arithmetic expressions? 了解所有细节和更多注意事项。
更一般地说,shell 算术表达式是一个雷区。另见:Security Implications of using unsanitized data in Shell Arithmetic evaluation.