配列変数をサポートする Bourne などのシェルでは、構文解析を使用して変数が配列であることを確認できます。
以下のコマンドはすべて実行後に実行されますa=(1 2 3)
。
zsh
:
$ declare -p a
typeset -a a
a=( 1 2 3 )
bash
:
$ declare -p a
declare -a a='([0]="1" [1]="2" [2]="3")'
ksh93
:
$ typeset -p a
typeset -a a=(1 2 3)
pdksh
そしてその派生物:
$ typeset -p a
set -A a
typeset a[0]=1
typeset a[1]=2
typeset a[2]=3
yash
:
$ typeset -p a
a=('1' '2' '3')
typeset a
次の例bash
:
if declare -p var 2>/dev/null | grep -q 'declare -a'; then
echo array variable
fi
この方法はあまりにも多くの作業を必要とし、サブシェルを作成する必要があります。=~
inなどの他のシェル組み込み機能を使用すると、サブシェルは[[ ... ]]
必要ありませんが、まだ複雑すぎます。
これを行うより簡単な方法はありますか?
答え1
私はあなたができるとは思わず、実際には何の違いもないと思います。
unset a
a=x
echo "${a[0]-not array}"
x
ksh93
これはとで同じことを行いますbash
。可能だと思うみんな変数はこれらのシェルの配列であるか、少なくとも特別な属性が割り当てられていない一般的な変数ですが、あまりチェックしていません。
マニュアルbash
では、割り当てを使用するときの配列と文字列変数のさまざまな動作について説明します+=
が、配列は次の状況でのみ異なる動作をするのを防ぎ、指定します。化合物コンテキストを割り当てます。
また、値が添え字に割り当てられると、変数は配列として扱われ、空の文字列の可能性が明示的に含まれていることを示します。上記では、通常の割り当てにより必ず添え字が割り当てられることがわかる。したがって、すべてが配列のようです。
実際には、次のものを使用できます。
[ 1 = "${a[0]+${#a[@]}}" ] && echo not array
...値が0の単一添え字のみが割り当てられたコレクション変数を明確に見つけます。
答え2
それでは、実際には中間部分だけが欲しく、declare -p
周辺部分は望んでいないというのでしょうか?
次のマクロを作成できます。
readonly VARTYPE='{ read __;
case "`declare -p "$__"`" in
"declare -a"*) echo array;;
"declare -A"*) echo hash;;
"declare -- "*) echo scalar;;
esac;
} <<<'
これにより、次のことができます。
a=scalar
b=( array )
declare -A c; c[hashKey]=hashValue;
######################################
eval "$VARTYPE" a #scalar
eval "$VARTYPE" b #array
eval "$VARTYPE" c #hash
(関数ローカル変数に使用したい場合は、関数だけでは機能しません)。
エイリアスあり
shopt -s expand_aliases
alias vartype='eval "$VARTYPE"'
vartype a #scalar
vartype b #array
vartype c #hash
答え3
zshから
zsh% a=(1 2 3) s=1
zsh% [[ ${(t)a} == *array* ]] && echo array
array
zsh% [[ ${(t)s} == *array* ]] && echo array
zsh%
答え4
~のため強く打つ、これは少しハッキングです(文書化されていますが)。以下を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
}
(bash-4.2以降)の使用に注意してください。これは、(syn.)がチェックしたい値のように動作または破損しないようにtypeset -g
関数に必要です。これは関数「変数」タイプも処理しないため、必要に応じて他の分岐テストを追加できます。typeset
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.
しかし、小さな問題があります。インデックスがゼロの単一の配列は、上記の2つの条件を満たします。 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
)で表されます。