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


14

配列変数をサポートするBourne like shellでは、変数が配列であるかどうかを確認するために何らかの解析を使用できます。

以下のすべてのコマンドは、実行後に実行されました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

このアプローチは非常に手間がかかり、サブシェルを作成する必要があります。以下のような他のシェルの組み込みを使用=~中は、[[ ... ]]サブシェルを必要としませんが、それでもあまりにも複雑になっています。

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


どのような状況で、変数が配列かどうかを確認する必要がありますか?
クサラナナンダ

回答:


10

私はあなたができるとは思わないし、それが実際に違いを生むとは思わない。

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

x

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

bash文字列変数対アレイの異なる動作に関するマニュアル会談使用+=、配列のみに異なった動作をすることを割り当て、それがその後のヘッジ及び状態化合物の割り当て状況。

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

実際には、おそらく次のものを使用できます:

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

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


だから${a[1]-not array}、タスクを実行できるかどうかをチェックすると思いますか?
cuonglm

@cuonglm-まあ、bashマニュアルによると:添え字に値が割り当てられている場合、配列変数は設定されていると見なされます。null文字列は有効な値です。添え字が割り当てられている場合、仕様ごとに配列が割り当てられます。あなたもできるからですa[5]=x[ 1 -eq "${#a[@]}" ] && [ -n "${a[0]+1}" ]うまくいくと思います。
mikeserv

6

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

@mikeserv良い点。エイリアスにより、見た目がきれいになります。+1
PSkocik

私が意味した-... alias vartype="$VARTYPE"またはまったく定義しない$VARTYPE-それはうまくいくはずですよね?スクリプトでの展開に関する仕様に違反するため、そのshoptことだけが必要です。bashalias
mikeserv

1
@mikeserv cuonglmは彼のニーズと好みに合わせてこのアプローチを微調整できると確信しています。;-)
PSkocik

...およびセキュリティに関する考慮事項。
PSkocik

上記のコード評価ユーザーがテキストを提供することはありません。それは機能と同じくらい安全です。関数を読み取り専用にすることに夢中になるのを見たことはありませんが、変数に読み取り専用のマークを付けることができます。
PSkocik

6

zshで

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

たぶんecho ${(t)var}簡単です。これをありがとう。

4

変数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にテスト済みの文字列が含まれている場合、これにより誤検知が発生します。


要素がゼロの配列はどうですか?
cuonglm

1
データ編集、カントー。非常に独創的です。:D
PSkocik

@cuonglmこのチェック( unset "var[0]" 2>/dev/null; ) && echo "var is an array."は、varがvar=()ゼロ要素の配列に設定されている場合、varが配列であることを正しく報告します。宣言とまったく同じように動作します。

スカラーがエクスポートされるか、整数/小文字/読み取り専用とマークされている場合、スカラーのテストは機能しません...他の空でない出力がスカラー変数を意味することをおそらく安全に確認できます。GNU grepへの依存を避けるためにgrep -E代わりに使用grep -Pします。
ステファンシャゼル

@StéphaneChazelas整数および/または小文字および/または読み取り専用のスカラーのテスト(bash)は-a、常に次のように始まりますdeclare -airl var='()'。したがって、grepテストは機能します。

3

以下のためのbashを使用しようとすると:それはハック(文書が)少しだtypeset「アレイ」属性を削除するには:

$ typeset +a BASH_VERSINFO
bash: typeset: BASH_VERSINFO: cannot destroy array variables in this way
echo $?
1

(でこれを行うことはできません。明示的に禁止さzshbashているため、配列をスカラーに変換できます。)

そう:

 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検査しようとしている値のように動作せず、上書きします。これは関数の「変数」タイプも処理しませんtypeset -f。必要に応じて別のブランチテストを追加できます。


もう1つの(ほぼ完全な)オプションは、これを使用することです。

    ${!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つの小さな問題があります。1つの添え字が0の配列は、上記の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 )

ただし、これもサブシェルが必要です。

もう1つの有用なテクニックはcompgen次のとおりです。

compgen -A arrayvar

これにより、すべてのインデックス付き配列がリストされますが、連想配列は特別に処理されず(bash-4.4まで)、通常の変数として表示されます(compgen -A variable


またtypeset +a、kshでエラーを報告します。ただし、zshではありません。

1

短い答え:

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

どちらも、配列を作成するために特別な宣言を必要としません。割り当てだけで十分であり、単純な割り当てvar=valueはと同じですvar[0]=value


試してくださいbash -c 'unset var; var=foo; typeset -p var'。bash answerは配列を報告しますか(-aが必要)?次と比較してくださいbash -c 'unset var; var[12]=foo; typeset -p var'。なぜ違いがあるのですか?A:シェルは、(良いか悪いかを問わず)どの変数がスカラーまたは配列であるかという概念を維持します。シェルkshは、両方の概念を1つにミックスします。

1

ヤシュの 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の場合、このアプローチは機能しません。エラーにつながる読み取り専用変数を変更しようとしています:

$ a=()
$ readonly a
$ array -d a
array: $a is read-only

0
#!/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
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.