Para o bash , é um pouco um hack (embora documentado): tente usar typeset
para remover o atributo "array":
$ typeset +a BASH_VERSINFO
bash: typeset: BASH_VERSINFO: cannot destroy array variables in this way
echo $?
1
(Você não pode fazer isso em zsh
, ele permite converter uma matriz em escalar, em bash
é explicitamente proibido.)
Então:
typeset +A myvariable 2>/dev/null || echo is assoc-array
typeset +a myvariable 2>/dev/null || echo is array
Ou em uma função, observando as advertências no final:
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
}
Observe o uso de typeset -g
(bash-4.2 ou posterior), isso é necessário dentro de uma função para que typeset
(syn. declare
) não funcione como local
e desconsidere o valor que você está tentando inspecionar. Isso também não lida com tipos de "variável" de função, você pode adicionar outro teste de ramificação usando typeset -f
, se necessário.
Outra opção (quase completa) é usar isto:
${!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.
Há um pequeno problema, porém, uma matriz com um único subscrito de 0 corresponde a duas das condições acima. Isso é algo que o mikeserv também faz referência, bash realmente não tem uma distinção strong, e algumas delas (se você verificar o Changelog) podem ser responsabilizadas pelo ksh e compatibilidade com como ${name[*]}
ou ${name[@]}
se comportam em um não array.
Portanto, uma solução parcial é:
if [[ ${!BASH_VERSINFO[*]} == '' ]]; then
echo no-such
elif [[ ${!BASH_VERSINFO[*]} == '0' ]]; then
echo not-array
elif [[ ${!BASH_VERSINFO[*]} != '0' ]];
echo is-array
fi
Eu usei no passado uma variação sobre isso:
while read _line; do
if [[ $_line =~ ^"declare -a" ]]; then
...
fi
done < <( declare -p )
isso também precisa de um subshell.
Uma técnica possivelmente mais útil é compgen
:
compgen -A arrayvar
Isto irá listar todos os arrays indexados, no entanto arrays associativos não são tratados especialmente (até bash-4.4) e aparecem como variáveis regulares ( compgen -A variable
)