对于bash来说,这有点hack(尽管有记录):尝试typeset
用于删除“ array”属性:
$ typeset +a BASH_VERSINFO
bash: typeset: BASH_VERSINFO: cannot destroy array variables in this way
echo $?
1
(您不能在中执行此操作zsh
,在bash
明确禁止的情况下,它允许您将数组转换为标量。)
所以:
typeset +A myvariable 2>/dev/null || echo is assoc-array
typeset +a myvariable 2>/dev/null || echo is array
或在函数中,注意最后的警告:
function typeof() {
local _myvar="$1"
if ! typeset -p $_myvar 2>/dev/null ; then
echo no-such
elif ! typeset -g +A $_myvar 2>/dev/null ; then
echo is-assoc-array
elif ! typeset -g +a $_myvar 2>/dev/null; then
echo is-array
else
echo scalar
fi
}
请注意使用typeset -g
(bash-4.2或更高版本),这在函数中是必需的,因此typeset
(syn。declare
)不能像local
您试图检查的值那样掩盖。这也不处理函数“变量”类型,您可以typeset -f
根据需要添加另一个分支测试。
另一个(几乎完整的)选项是使用此选项:
${!name[*]}
If name is an array variable, expands to the list
of array indices (keys) assigned in name. If name
is not an array, expands to 0 if name is set and
null otherwise. When @ is used and the expansion
appears within double quotes, each key expands to a
separate word.
但是,有一个小问题,单个下标为0的数组与上述条件中的两个匹配。这是mikeserv还引用的内容,bash确实没有硬性区别,其中某些(如果您检查Changelog)(可以检查klog)可以归因于ksh,并且可以对非数组的行为${name[*]}
或${name[@]}
行为负责。
因此,部分解决方案是:
if [[ ${!BASH_VERSINFO[*]} == '' ]]; then
echo no-such
elif [[ ${!BASH_VERSINFO[*]} == '0' ]]; then
echo not-array
elif [[ ${!BASH_VERSINFO[*]} != '0' ]];
echo is-array
fi
过去,我使用了一个变体:
while read _line; do
if [[ $_line =~ ^"declare -a" ]]; then
...
fi
done < <( declare -p )
这也需要一个子shell。
一种更有用的技术是compgen
:
compgen -A arrayvar
这将列出所有索引数组,但是关联数组未经过特殊处理(最高bash-4.4),并显示为常规变量(compgen -A variable
)