Для 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
).