エイリアスあり

エイリアスあり

配列変数をサポートする 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関数に必要です。これは関数「変数」タイプも処理しないため、必要に応じて他の分岐テストを追加できます。typesetdeclarelocaltypeset -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)で表されます。

関連情報