要素がbashの配列にあるかどうかをテストします


17

配列にbashの要素があるかどうかを確認する良い方法はありますか?

または、数値または文字列が一連の定義済み定数のいずれかに等しいかどうかを確認する別の方法はありますか?

回答:


24

Bash 4では、連想配列を使用できます。

# set up array of constants
declare -A array
for constant in foo bar baz
do
    array[$constant]=1
done

# test for existence
test1="bar"
test2="xyzzy"

if [[ ${array[$test1]} ]]; then echo "Exists"; fi    # Exists
if [[ ${array[$test2]} ]]; then echo "Exists"; fi    # doesn't

最初に配列を設定するには、直接割り当てを行うこともできます。

array[foo]=1
array[bar]=1
# etc.

またはこの方法:

array=([foo]=1 [bar]=1 [baz]=1)

実際、値が空の場合、[[]]テストは機能しません。例えば、「array ['test'] = ''」。この場合、キー 'test'が存在し、$ {!array [@]}でリストされていますが、「[[$ {array ['test']}]]; echo $?」0ではなく1をエコーし​​ます。
haridsv11年

1
${array[$test1]}は簡単ですが、問題がありset -uます。スクリプトで使用する場合(推奨)、「非バインド変数」を取得するため、機能しません。
トクランド

@tokland:誰がそれを勧めますか?私は確かにそうしません。
追って通知があるまで一時停止します。

@DennisWilliamson:[OK]を、何人かの人々はそれをお勧めしますが、私は関係なく、これらのフラグの値を機能するソリューションを持っていることは素晴らしいことだと思います。
トックランド


10

これは古い質問ですが、最も簡単な解決策はまだ登場していないと思いますtest ${array[key]+_}。例:

declare -A xs=([a]=1 [b]="")
test ${xs[a]+_} && echo "a is set"
test ${xs[b]+_} && echo "b is set"
test ${xs[c]+_} && echo "c is set"

出力:

a is set
b is set

この作品はチェック方法を確認するには、これを


2
情報マニュアルenvでは、「test」という名前を採用した可能性のあるエイリアス、プログラム、およびその他の関数のあいまいさを避けるために使用することをお勧めします。上記のようにenv test ${xs[a]+_} && echo "a is set"。ダブルブラケットを使用してこの機能を取得することもできます。同じトリックを使用してnullをチェックします[[ ! -z "${xs[b]+_}" ]] && echo "b is set"
。– A.Danischewski

また、あなたはもっと簡単に使用することができます[[ ${xs[b]+set} ]]
アルネ・L.

5

連想配列の要素が存在する(設定されていない)かどうかをテストする方法があります。これは空とは異なります。

isNotSet() {
    if [[ ! ${!1} && ${!1-_} ]]
    then
        return 1
    fi
}

次にそれを使用します:

declare -A assoc
KEY="key"
isNotSet assoc[${KEY}]
if [ $? -ne 0 ]
then
  echo "${KEY} is not set."
fi

ちょうど注記:DECLARE -Aはbashの3.2.39(Debianのlennyの)上の仕事をしませんが、それはbashの4.1.5で動作します(Debianのスクイズ)
パヴェルPolewicz

連想配列は、バッシュ4に導入された
ディエゴF.デュラン

1
if ! some_check then return 1=に注意してくださいsome_check。だから:isNotSet() { [[ ... ]] }。以下の私の解決策を確認してください、あなたは簡単なチェックでそれを行うことができます。
トックランド

3

配列の内容をgrepにパイプすることで、エントリが存在するかどうかを確認できます。

 printf "%s\n" "${mydata[@]}" | grep "^${val}$"

一致の行番号を返すgrep -nを使用してエントリのインデックスを取得することもできます(1を減算してゼロベースのインデックスを取得することを忘れないでください)。これは非常に大きな配列を除いてかなり高速です。

# given the following data
mydata=(a b c "hello world")

for val in a c hello "hello world"
do
           # get line # of 1st matching entry
    ix=$( printf "%s\n" "${mydata[@]}" | grep -n -m 1 "^${val}$" | cut -d ":" -f1 )

    if [[ -z $ix ]]
    then
        echo $val missing
    else
         # subtract 1.  Bash arrays are zero-based, but grep -n returns 1 for 1st line, not 0 
        echo $val found at $(( ix-1 ))
    fi
done

a found at 0
c found at 2
hello missing
hello world found at 3

説明:

  • $( ... ) コマンドの出力を変数にキャプチャするためにバックティックを使用するのと同じです
  • printf mydataを1行に1要素出力します
  • (すべての引用符が必要@です。*. これに加えて、「hello world」を2行に分割することを避けます)
  • grep正確な文字列を検索します: ^そして、$行頭と行末に一致します
  • grep -n 4:hello worldの形式で行番号を返します
  • grep -m 1 最初の一致のみを検索します
  • cut 行番号のみを抽出します
  • 返された行番号から1を引きます。

もちろん、減算をコマンドに組み込むことができます。ただし、欠落がないか-1をテストします。

ix=$(( $( printf "%s\n" "${mydata[@]}" | grep -n -m 1 "^${val}$" | cut -d ":" -f1 ) - 1 ))

if [[ $ix == -1 ]]; then echo missing; else ... fi
  • $(( ... )) 整数演算を行います

1

配列内のデータが非常に限られている場合を除き、ループせずに適切に実行できるとは思わない。

これは単純なバリアントの1つです。これは"Super User"、配列に存在することを正しく示しています。しかし、それは"uper Use"配列内にもあると言うでしょう。

MyArray=('Super User' 'Stack Overflow' 'Server Fault' 'Jeff' );
FINDME="Super User"

FOUND=`echo ${MyArray[*]} | grep "$FINDME"`

if [ "${FOUND}" != "" ]; then
  echo Array contains: $FINDME
else
  echo $FINDME not found
fi

#
# If you where to add anchors < and > to the data it could work
# This would find "Super User" but not "uper Use"
#

MyArray2=('<Super User>' '<Stack Overflow>' '<Server Fault>' '<Jeff>' );

FOUND=`echo ${MyArray2[*]} | grep "<$FINDME>"`

if [ "${FOUND}" != "" ]; then
  echo Array contains: $FINDME
else
  echo $FINDME not found
fi

問題は、配列をループする以外に、アンカーを追加する簡単な方法(考えられること)がないことです。配列に入れる前に追加できない限り...


ただし、定数が英数字の場合は(でgrep "\b$FINDME\b")良い解決策です。おそらくで、何のスペースを持っていない英数字以外の定数で仕事ができる"(^| )$FINDME(\$| )"(またはそのような何か...私は正規表現のgrepの用途の何味を学ぶことができたことはありません)
TGR

1
#!/bin/bash
function in_array {
  ARRAY=$2
  for e in ${ARRAY[*]}
  do
    if [[ "$e" == "$1" ]]
    then
      return 0
    fi
  done
  return 1
}

my_array=(Drupal Wordpress Joomla)
if in_array "Drupal" "${my_array[*]}"
  then
    echo "Found"
  else
    echo "Not found"
fi

1
このアプローチを提案している理由について詳しく説明していただけますか?OPは配列をループすることなくそれする方法があるかどうか尋ねましたin_array。乾杯
ベルティエブ

まあ、少なくともそのループは関数にカプセル化されています。これは多くの場合(小さなデータセットで)十分であり、bash 4+を必要としません。おそらく${ARRAY[@]}使用する必要があります。
トバイアス
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.