シェルによるアレイサポートのテスト


12

コマンドラインでローカルのBourneのようなシェルによる配列サポートをテストする簡潔な方法はありますか?

これは常に可能です:

$ arr=(0 1 2 3);if [ "${arr[2]}" != 2 ];then echo "No array support";fi

または$SHELLシェルバージョンのテスト:

$ eval $(echo "$SHELL --version") | grep version

そして、私がそれにアクセスできると仮定して、manページを読みます。(そこからも、からの執筆で/bin/bash、私はすべてのBourneのようなシェルが長いオプションを認めていると仮定しています--version、それが例えば kshで壊れるとき。)

スクリプトの最初またはそれを呼び出す前に、無人でUsageセクションに組み込むことができる簡単なテストを探しています。


ボーンのようなシェルに限定したいと思いますか?
ステファンChazelas

@StéphaneChazelas:はい、もしあなたが(徹底的ではなく)コアグループであるsh、csh、ksh、tcsh、bash、zshと親しい友人からなるという意味であれば。ヤシュがこの星座のどこに位置するのかはわかりません。
Cbhihe、2015年

2
cshボーンシェルではありません。 tcshどちらでもありません(cshいくつかのバグが修正されています)
cas

1
$SHELLユーザーのシェルれる好ましいですが、のように$EDITOR自分の好みのテキストエディタです。現在実行中のシェルとはほとんど関係ありません。
ステファンChazelas

1
evalの出力を$SHELL --versionシェルコードとして使用しても意味がありません。
ステファンChazelas

回答:


12

あなたはボーンのようなシェル(他の多くのシェルが好きに制限したいと仮定するとcshtcshrcesまたはfishサポートアレイが、ボーンのようなシェルを同時に互換性のあるスクリプトを書いて、彼らは完全に異なるために通訳されているとして、それらはトリッキーかつ一般的に無意味であると互換性のない言語)、実装間で大きな違いがあることに注意してください。

配列をサポートするBourneのようなシェルは次のとおりです。

  • ksh88(これは配列を実装する最初の1つです。ksh88はksh、ほとんどの従来の商用Unicesと同じようにまだ見つかりますsh

    • 配列は1次元です
    • 配列は、またはで始まらないことを保証できない場合、set -A array foo barまたはとして定義されset -A array -- "$var" ...ます$var-+
    • 配列のインデックスは 0ます。
    • 個々の配列要素はとして割り当てられa[1]=valueます。
    • 配列はまばらです。これは、設定されていなくa[5]=fooても機能し、a[0,1,2,3,4]未設定のままにします。
    • ${a[5]}インデックス5の要素にアクセスするには(配列がスパースの場合、必ずしも6番目の要素とは限りません)。5任意の演算式が存在することができます。
    • 配列のサイズと添え字は制限されています(4096まで)。
    • ${#a[@]} 配列内の割り当てられた要素の数です(割り当てられた最大のインデックスではありません)。
    • 割り当てられた添え字のリストを知る方法はありません(4096要素をで個別にテストする以外[[ -n "${a[i]+set}" ]])。
    • $aと同じ${a[0]}です。つまり、配列は、スカラー変数に追加の値を与えることで、なんとかして拡張します。
  • pdkshと派生物(それはkshshは、いくつかのBSD BSD ksh93ソースが解放される前の唯一のオープンソースksh実装でした):

    ほとんどが好きですksh88が、注意してください:

    • いくつかの古い実装がサポートしていませんでしたset -A array -- foo bar(、--そこに必要とされていませんでした)。
    • ${#a[@]}割り当てられた最大のインデックスの1プラスです。(a[1000]=1; echo "${#a[@]}"配列に要素が1つしかない場合でも1001を出力します。
    • 新しいバージョンでは、配列サイズが制限されなくなりました(整数のサイズによる以外)。
    • 最近のバージョンは、mkshからインスピレーションを得たいくつかの余分な演算子を持っているbashksh93またはzshラの割り当てのようなa=(x y)a+=(z)${!a[@]}割り当てられたインデックスのリストを取得します。
  • zshzshアレイは一般に設計が優れておりkshcshアレイを最大限に活用します。これらは似てkshいますが、大きな違いがあります。

    • インデックスは0ではなく1から始まります(kshエミュレーションを除く)。これはBourne配列(位置パラメーター$ @、これzshは$ argv配列としても公開されcshます)および配列と一致しています。
    • それらは通常の/スカラー変数とは別のタイプです。演算子はそれらに別様に適用され、あなたが一般的に期待するように。$aと同じではありません${a[0]}が、配列の空でない要素に展開さ"${a[@]}"れます(のようなすべての要素に対してksh)。
    • これらはスパース配列ではなく、通常の配列です。a[5]=1機能しますが、割り当てられていない場合は、1から4までのすべての要素に空の文字列を割り当てます。したがって、${#a[@]}${#a}kshでindice 0の要素のサイズと同じ)は、配列内の要素の数であり、割り当てられた最大のindiceです。
    • 連想配列がサポートされています。
    • 配列を扱う多数の演算子がサポートされていますが、ここにリストするには大きすぎます。
    • として定義されa=(x y)た配列。set -A a x yも機能しますがset -A a -- x y、kshエミュレーション以外ではサポートされ--ません(zshエミュレーションでは必要ありません)。
  • ksh93。(ここでは最新バージョンについて説明しています)。ksh93、FOSSとしてリリースされた今、長い間検討されてきた実験的なシステムがますます多くのシステムで見られるようになりました。たとえば、それは/bin/sh(それがBourneシェルに取って代わった場所で/usr/xpg4/bin/shあり、POSIXシェルは依然としてに基づいていますksh88)およびkshof Solaris 11です。その配列はksh88を拡張および強化します。

    • a=(x y)は配列の定義に使用できますが、a=(...)複合変数(a=(foo=bar bar=baz))の定義にも使用されるため、a=()あいまいであり、配列ではなく複合変数を宣言します。
    • 配列は多次元(a=((0 1) (0 2)))であり、配列要素は複合変数(a=((a b) (c=d d=f)); echo "${a[1].c}")にすることもできます。
    • a=([2]=foo [5]=bar)構文は、一度に疎な配列を定義するために使用することができます。
    • サイズ制限が解除されました。
    • の範囲ではありませんが、zsh配列を操作するためにサポートされている多数の演算子。
    • "${!a[@]}" 配列インデックスのリストを取得します。
    • 連想配列も別の型としてサポートされています。
  • bashbashGNUプロジェクトのシェルです。それは次のように使われているshOS / XおよびいくつかのGNU / Linuxディストリビューションの最新バージョンに。bash配列は主ksh88ksh93およびのいくつかの機能を備えたものをエミュレートしますzsh

    • a=(x y)サポートされています。サポートされてset -A a x y いませんa=()空の配列を作成します(には複合変数はありませんbash)。
    • "${!a[@]}" インデックスのリスト。
    • a=([foo]=bar)ksh93およびからのいくつかの他の構文と同様にサポートされている構文zsh
    • 最近のbashバージョンでは、別のタイプとして連想配列もサポートしています。
  • yash。これは、比較的最近のクリーンでマルチバイト対応のPOSIX sh実装です。広く使用されていません。その配列は、次のようなクリーンなAPIです。zsh

    • 配列はスパースではありません
    • 配列インデックスは1から始まります
    • で定義(および宣言) a=(var value)
    • array組み込みで挿入、削除、または変更された要素
    • array -s a 5 valueその要素が事前に割り当てられていない場合、5 番目の要素を変更することは失敗します。
    • 配列内の要素の数は${a[#]}${#a[@]}リストのような要素のサイズです。
    • 配列は別のタイプです。a=("$a")要素を追加または変更する前に、スカラー変数を配列として再定義する必要があります。
    • として呼び出されshた場合、配列はサポートされません。

したがって、そこから、配列サポートを検出することがわかります。

if (unset a; set -A a a; eval "a=(a b)"; eval '[ -n "${a[1]}" ]'
   ) > /dev/null 2>&1
then
  array_supported=true
else
  array_supported=false
fi

これらの配列を使用するには十分ではありません。ラッパーコマンドを定義して配列全体および個々の要素を割り当てる必要があり、スパース配列を作成しないようにします。

お気に入り

unset a
array_elements() { eval "REPLY=\"\${#$1[@]}\""; }
if (set -A a -- a) 2> /dev/null; then
  set -A a -- a b
  case ${a[0]}${a[1]} in
    --) set_array() { eval "shift; set -A $1"' "$@"'; }
        set_array_element() { eval "$1[1+(\$2)]=\$3"; }
        first_indice=0;;
     a) set_array() { eval "shift; set -A $1"' -- "$@"'; }
        set_array_element() { eval "$1[1+(\$2)]=\$3"; }
        first_indice=1;;
   --a) set_array() { eval "shift; set -A $1"' "$@"'; }
        set_array_element() { eval "$1[\$2]=\$3"; }
        first_indice=0;;
    ab) set_array() { eval "shift; set -A $1"' -- "$@"'; }
        set_array_element() { eval "$1[\$2]=\$3"; }
        first_indice=0;;
  esac
elif (eval 'a[5]=x') 2> /dev/null; then
  set_array() { eval "shift; $1=("'"$@")'; }
  set_array_element() { eval "$1[\$2]=\$3"; }
  first_indice=0
elif (eval 'a=(x) && array -s a 1 y && [ "${a[1]}" = y ]') 2> /dev/null; then
  set_array() { eval "shift; $1=("'"$@")'; }
  set_array_element() {
    eval "
      $1=(\${$1+\"\${$1[@]}"'"})
      while [ "$(($2))" -ge  "${'"$1"'[#]}" ]; do
        array -i "$1" "$2" ""
      done'
    array -s -- "$1" "$((1+$2))" "$3"
   }
  array_elements() { eval "REPLY=\${$1[#]}"; }
  first_indice=1
else
  echo >&2 "Array not supported"
fi

そして、あなたは、配列を持つ要素にアクセス"${a[$first_indice+n]}"して、リスト全体を"${a[@]}"ラッパー関数を使用(array_elementsset_arrayset_array_element)(の配列の要素数を取得するために$REPLY)、全体またはアサイン個々の要素として配列を設定します。

おそらく努力する価値はありません。perlBourne / POSIXシェル配列を使用または制限します"$@"

内部的に配列を使用する関数を定義するために、ユーザーの対話型シェルがソースとなるファイルを作成することが目的である場合は、役立ついくつかの注意事項を次に示します。

配列をローカルスコープ(関数または無名関数内)の配列のzshように構成できますksh

myfunction() {
  [ -z "$ZSH_VERSION" ] || setopt localoption ksharrays
  # use arrays of indice 0 in this function
}

また、以下を使用してエミュレートすることもできますkshksh配列および他のいくつかの領域との互換性を向上させます)。

myfunction() {
  [ -z "$ZSH_VERSION" ] || emulate -L ksh
  # ksh code more likely to work here
}

このことを念頭に置いて、あなたにしているのサポートを落として喜んでyashksh88や古いバージョンのpdksh誘導体は、と長いあなたがまばらなアレイを作成しようとしないようとして、あなたは一貫して使用することができるはずです。

  • a[0]=foo
  • a=(foo bar)(ではないa=()
  • "${a[#]}""${a[@]}""${a[0]}"

ユーザーがまだ自分の配列を通常はzshの方法で使用しているemulate -L ksh間、zsh


7

eval配列構文を試すために使用できます:

is_array_support() (
  eval 'a=(1)'
) >/dev/null 2>&1

if is_array_support; then
  echo support
else
  echo not
fi

2
ksh88は配列をサポートしますが、はサポートしませんa=()。ではksh93a=()変数が事前に配列として宣言されていない限り、配列ではなく複合変数を宣言します。
ステファンChazelas

2
また、配列の実装には大きな違いがあることに注意してください。たとえば、0から始まる配列インデックスを持つもの(kshエミュレーションではbash、ksh、zsh)、1つから始まるもの(zsh、yash)などがあります。一部は通常の配列/リストであり、一部は疎配列です(kshまたはbashのように正の整数に制限されたキーを持つ連想配列)。
ステファンChazelas

ではyash、あなたはいたしませんa[5]=1が、array -s a 5 1
ステファン・Chazelas

@StéphaneChazelas:精度に感謝します。私の場合、すべては、配列(連想配列であるかどうか)がサポートされているかどうかで決まります。index-baseに関する詳細は、無人で実行することを意図したスクリプトでも簡単に解決できます。
Cbhihe、2015年

@StéphaneChazelas:の複合変数ksh93は私を驚かせました。それについてのドキュメントの一部を教えていただけませんか。1配列に追加して機能させます。
cuonglm 2015年
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.