配列変数をサポートするシェルのような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
このアプローチは手間がかかりすぎ、サブシェルを生成する必要があります。 =~
の[[ ... ]]
などの他の組み込みシェルを使用する場合、サブシェルは必要ありませんが、それでも複雑すぎます。
このタスクを達成する簡単な方法はありますか?
私はあなたができるとは思いません、そしてそれが実際にどんな違いをもたらすとは思いません。
unset a
a=x
echo "${a[0]-not array}"
x
これは、ksh93
とbash
のどちらでも同じことを行います。おそらくall変数はこれらのシェルの配列、または少なくとも特別な属性が割り当てられていない通常の変数ですが、その多くはチェックしていません。
bash
マニュアルは、+=
割り当てを使用する場合の配列と文字列変数の異なる動作について説明していますが、その後、ヘッジは配列がcompoundでのみ異なる動作をすることを示しています=割り当てコンテキスト。
また、添え字に値が割り当てられている場合、変数は配列と見なされ、明示的にnull文字列の可能性が含まれます。上記では、通常の割り当てによって添字が確実に割り当てられることがわかります。つまり、すべてが配列であると思います。
実際には、おそらく以下を使用できます。
[ 1 = "${a[0]+${#a[@]}}" ] && echo not array
...値0の単一の添え字のみが割り当てられているセット変数を明確に特定します。
だからあなたは事実上_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
Zshで
zsh% a=(1 2 3) s=1
zsh% [[ ${(t)a} == *array* ]] && echo array
array
zsh% [[ ${(t)s} == *array* ]] && echo array
zsh%
変数varをテストするには、
b=("${!var[@]}")
c="${#b[@]}"
複数の配列インデックスがあるかどうかをテストすることは可能です:
[[ $c > 1 ]] && echo "Var is an array"
最初のインデックス値がゼロでない場合:
[[ ${b[0]} -eq 0 ]] && echo "Var is an array" ## should be 1 for zsh.
唯一の難しい混乱は、インデックス値が1つしかなく、その値がゼロ(または1)である場合です。
その条件の場合、配列ではない変数から配列要素を削除しようとするという副作用を使用することができます。
**bash** reports an error with unset var[0]
bash: unset: var: not an array variable
**zsh** also reports an error with $ var[1]=()
attempt to assign array value to non-array
これはbashでは正しく機能します。
# Test if the value at index 0 could be unset.
# If it fails, the variable is not an array.
( unset "var[0]" 2>/dev/null; ) && echo "var is an array."
Zshの場合、インデックスは1である必要があります(互換モードがアクティブでない場合)。
サブシェルは、varのインデックス0を消去する副作用を回避するために必要です。
Kshで動作させる方法はありません。
この関数はbash4.2以降でのみ機能します
getVarType(){
varname=$1;
case "$(typeset -p "$varname")" in
"declare -a"*|"typeset -a"*) echo array; ;;
"declare -A"*|"typeset -A"*) echo hash; ;;
"declare -- "*|"typeset "$varname*| $varname=*) echo scalar; ;;
esac;
}
var=( foo bar ); getVarType var
これはbash4.2以降でのみ機能します
{ typeset -p var | grep -qP '(declare|typeset) -a'; } && echo "var is an array"
注:varにテスト済みの文字列が含まれている場合、これは誤検知になります。
bashの場合、これは少しハックです(文書化されていますが):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
およびclobberは、検査しようとしている値です。これは関数の「変数」タイプも処理しません。必要に応じて、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.
ただし、わずかに1つの問題があります。添え字が0の配列は、上記の条件の2つに一致します。これは、mikeservも参照するものであり、bashには実際に明確な区別はありません。これの一部(変更ログを確認した場合)は、kshのせいであり、${name[*]}
または${name[@]}
の動作との互換性があります。非配列上。
したがって、partialソリューションは次のとおりです。
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 )
これもサブシェルが必要です。
もう1つの便利なテクニックはcompgen
です。
compgen -A arrayvar
これはすべてのインデックス付き配列をリストしますが、連想配列は特別に処理されず(bash-4.4まで)、通常の変数として表示されます(compgen -A variable
)
短い答え:
この表記法を導入した2つのシェル(bash
およびksh93
)の場合、スカラー変数は単一の要素を持つ配列です。
create配列に対する特別な宣言も必要ありません。割り当てだけで十分であり、単純な割り当てvar=value
はvar[0]=value
と同じです。
yashのarray
組み込み には、配列変数でのみ機能するオプションがいくつかあります。例:-d
オプションは非配列変数のエラーを報告します:
$ a=123
$ array -d a
array: no such array $a
したがって、次のようなことができます。
is_array() (
array -d -- "$1"
) >/dev/null 2>&1
a=(1 2 3)
if is_array a; then
echo array
fi
b=123
if ! is_array b; then
echo not array
fi
この方法は、配列変数がreadonlyの場合は機能しません。 readonly変数を変更しようとすると、エラーが発生します。
$ a=()
$ readonly a
$ array -d a
array: $a is read-only
#!/bin/bash
var=BASH_SOURCE
[[ "$(declare -pa)" =~ [^[:alpha:]]$var= ]]
case "$?" in
0)
echo "$var is an array variable"
;;
1)
echo "$var is not an array variable"
;;
*)
echo "Unknown exit code"
;;
esac