Bash函数内调用set获取变量列表结果异常问题排查
问题原因
核心是Bash数组的引用规则:直接使用数组变量名(如$v)不指定下标时,Bash只会返回数组的第0个元素,不会返回全部数组内容。
外部逻辑正常的原因
第一版代码能正常输出的逻辑链路是:
- 函数内将
set返回的所有变量按行读入数组v后,通过echo "${v[*]}"把数组所有元素拼接成单个字符串输出 - 函数外部用
x=$(getDeclared)接住完整的拼接字符串,再通过x=($x)拆分为数组,遍历自然能拿到全部变量
函数内逻辑失效的原因
把遍历逻辑移入函数后,你跳过了「数组转字符串输出、外部接收再拆分数组」的步骤,直接写了x=($v):
- 此处
$v等价于${v[0]},只会取数组v的第一个元素,也就是BASH=/usr/local/bin/bash这一行 - 最终数组
x仅包含这一个元素,无论加不加下划线过滤,遍历都只会输出这一行,和你预期不符
注:Shellcheck给出的加引号修改建议不适用这个场景,因为Shellcheck默认把
v识别为普通字符串变量,没识别到它是数组,所以按照提示改成x="($v)"只会给第一个元素外面套一层括号,完全达不到复制数组的效果。
修复方法
在函数内复制数组时,使用Bash原生的全数组展开语法,把原来的x=($v)替换为:
x=("${v[@]}")
该写法会把数组v的所有元素完整复制到数组x中,不会出现只取第一个元素的问题。
修复后的完整函数代码如下:
#!/usr/local/bin/bash getDeclared() { local v local x v=$( set -o posix ; set ) IFS=$'\n' read -d '\034' -r -a v <<<"${v}\034" # 完整复制数组所有元素 x=("${v[@]}") for ((y=0;y<${#x[@]};y++)); do [ "${x[y]::1}" == "_" ] && echo "$y: ${x[y]}"$'\n' done } declare -a _hints=() declare _hint="start.hint" declare _paths declare _basepath="root/devops/cloud" declare _base="/" declare _hintmaxdepth=0 echo "${BASH_VERSION}" getDeclared
运行后就能得到和第一版代码完全一致的输出结果。
内容的提问来源于stack exchange,提问作者Brian Patterson
相关产品推荐
相关产品推荐

