Bash 函数为什么只收到两个关联数组键?如何正确传递全部键?
第二个参数在两次调用中的含义不一致:函数需要数组变量名,而 "${!associative_array[@]}" 会把键直接展开为多个参数。第一个键被当作变量名,随后又被 shift 2 移除,因此最终命令中只出现了另外两个键。
可以保留现有函数接口:先将关联数组的键存入普通索引数组,再把该数组的变量名传给函数。
为什么直接展开键会少一个
函数开头约定了两个固定参数:
local -r enable_logging="$1"
local -n parameter_names="$2"
shift 2
local -n 创建变量引用,要求 $2 是被引用变量的名称;它不会把后面的多个参数收集成数组。
原调用:
run_aws_ssm_delete_parameters true "${!associative_array[@]}" --color on
会将每个键展开成独立参数。假设这次键的展开顺序是 two three one,实际传参相当于:
run_aws_ssm_delete_parameters true two three one --color on
函数随后会:
- 将
true作为日志开关。 - 让
parameter_names引用名为two的变量。 - 用
shift 2移除true和two。 - 通过
aws_cli_flags+=("$@"),将剩余的three one --color on追加到命令参数中。
如果变量 two 未定义,"${parameter_names[@]}" 就不会提供数组元素。输出中的 three one 来自剩余的位置参数,并非正确引用后的数组展开结果。
关联数组的遍历顺序没有固定保证,不能依赖某个键总是先出现。如果第一个键不是合法的变量引用,或者恰好对应已有变量,还可能得到其他结果。
保留现有函数:先把键放入索引数组
将 main 改为下面这样,函数中的 "${parameter_names[@]}" 可以保持原样:
function main() {
local -a normal_array=("one" "two" "three")
run_aws_ssm_delete_parameters true normal_array --color on
local -A associative_array=(
["one"]="value_one"
["two"]="value_two"
["three"]="value_three"
)
local -a associative_keys=("${!associative_array[@]}")
run_aws_ssm_delete_parameters true associative_keys --color on
}
main "$@"
两次调用的第二个参数现在都是数组变量名。函数通过 nameref 读取索引数组中的元素,shift 2 后剩下的都是附加参数。
保留 "${!associative_array[@]}" 外面的双引号,可以确保每个键都作为单独且完整的数组元素传递。
为什么传入关联数组名后得到的是值
下面的调用正确传递了变量名:
run_aws_ssm_delete_parameters true associative_array --color on
但函数使用的是:
"${parameter_names[@]}"
对于关联数组,这个表达式展开的是值。要读取键,应使用:
"${!parameter_names[@]}"
如果函数专门接收关联数组,可以把构造命令参数的部分改为:
local -a aws_cli_flags=(
"delete-parameters"
"--names"
"${!parameter_names[@]}"
)
然后直接传入 associative_array 即可。
这个修改也会影响普通索引数组:对索引数组使用 "${!parameter_names[@]}",得到的是 0 1 2 这样的下标。如果需要同时支持题目中的两种调用,先生成 associative_keys 更符合现有函数的用途。
为什么错误输出是 Bad: 0
原代码的错误分支是:
else
set +x
printf "Bad: %s" "$?"
return 1
fi
这里的 $? 读取的是刚执行完的 set +x 的状态,AWS 命令的失败状态已经被覆盖。应在进入错误分支后立即保存状态,再关闭跟踪:
local result status
set -x
if result="$(aws ssm "${aws_cli_flags[@]}")"; then
set +x
[[ "${enable_logging}" = true ]] && printf "Good: %s\n" "${result}"
return 0
else
status=$?
set +x
printf "Bad: %s\n" "$status" >&2
return "$status"
fi
这样可以保留 AWS CLI 的退出状态。凭证错误仍需单独解决;参数跟踪只能说明命令收到了哪些参数,不能说明删除操作成功。
确认脚本由 Bash 执行
这段代码使用 Bash 的关联数组和 local -n,需要支持这些特性的 Bash;local -n 要求 Bash 4.3 或更高版本。
终端的交互 shell 是 zsh,并不意味着脚本也由 zsh 执行。使用 bash script.sh,或直接执行带有 #!/usr/bin/env bash 的可执行脚本,会按相应方式启动 Bash。zsh script.sh 则会使用 zsh,不能按这里的 Bash 语义分析。
可以检查实际使用的 Bash 版本:
bash --version
备注:内容仅供参考。
