web-dev-qa-db-ja.com

チェック変数はシェルのようなボーンの配列ですか?

配列変数をサポートするシェルのような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

このアプローチは手間がかかりすぎ、サブシェルを生成する必要があります。 =~[[ ... ]]などの他の組み込みシェルを使用する場合、サブシェルは必要ありませんが、それでも複雑すぎます。

このタスクを達成する簡単な方法はありますか?

14
cuonglm

私はあなたができるとは思いません、そしてそれが実際にどんな違いをもたらすとは思いません。

unset a
a=x
echo "${a[0]-not array}"

x

これは、ksh93bashのどちらでも同じことを行います。おそらくall変数はこれらのシェルの配列、または少なくとも特別な属性が割り当てられていない通常の変数ですが、その多くはチェックしていません。

bashマニュアルは、+=割り当てを使用する場合の配列と文字列変数の異なる動作について説明していますが、その後、ヘッジは配列がcompoundでのみ異なる動作をすることを示しています=割り当てコンテキスト。

また、添え字に値が割り当てられている場合、変数は配列と見なされ、明示的にnull文字列の可能性が含まれます。上記では、通常の割り当てによって添字が確実に割り当てられることがわかります。つまり、すべてが配列であると思います。

実際には、おそらく以下を使用できます。

[ 1 = "${a[0]+${#a[@]}}" ] && echo not array

...値0の単一の添え字のみが割り当てられているセット変数を明確に特定します。

10
mikeserv

だからあなたは事実上_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
6
PSkocik

Zshで

zsh% a=(1 2 3) s=1
zsh% [[ ${(t)a} == *array* ]] && echo array
array
zsh% [[ ${(t)s} == *array* ]] && echo array
zsh%
6
llua

変数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で動作させる方法はありません。

編集1

この関数は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

編集2

これはbash4.2以降でのみ機能します

{ typeset -p var | grep -qP '(declare|typeset) -a'; } && echo "var is an array"

注:varにテスト済みの文字列が含まれている場合、これは誤検知になります。

4
user79743

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

3
mr.spuratic

短い答え:

この表記法を導入した2つのシェル(bashおよびksh93)の場合、スカラー変数は単一の要素を持つ配列です。

create配列に対する特別な宣言も必要ありません。割り当てだけで十分であり、単純な割り当てvar=valuevar[0]=valueと同じです。

1
Henk Langeveld

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
1
cuonglm
#!/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
0
Fólkvangr