Для bash это немного хак (хотя и задокументировано): попытка использовать typesetдля удаления атрибута «массив»:
$ 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 на самом деле не имеет жестких различий, и некоторые из них (если вы посмотрите журнал изменений) можно обвинить в 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 )
это тоже нуждается в подоболочке.
Еще один, возможно, полезный метод compgen:
compgen -A arrayvar
При этом будут перечислены все проиндексированные массивы, однако ассоциативные массивы не обрабатываются специально (до bash-4.4) и отображаются как обычные переменные ( compgen -A variable).