Bash配列に値が含まれているかどうかを確認する


443

Bashで、配列に特定の値が含まれているかどうかをテストする最も簡単な方法は何ですか?

編集:回答とコメントの助けを借りて、いくつかのテストの後、私はこれを思いつきました:

function contains() {
    local n=$#
    local value=${!n}
    for ((i=1;i < $#;i++)) {
        if [ "${!i}" == "${value}" ]; then
            echo "y"
            return 0
        fi
    }
    echo "n"
    return 1
}

A=("one" "two" "three four")
if [ $(contains "${A[@]}" "one") == "y" ]; then
    echo "contains one"
fi
if [ $(contains "${A[@]}" "three") == "y" ]; then
    echo "contains three"
fi

それが最善の解決策かどうかはわかりませんが、うまくいくようです。

回答:


457

このアプローチには、すべての要素を(少なくとも明示的には)ループする必要がないという利点があります。しかしarray_to_string_internal()array.cでは依然として配列要素をループし、それらを文字列に連結するため、提案されたループソリューションよりも効率的ではないかもしれませんが、より読みやすくなっています。

if [[ " ${array[@]} " =~ " ${value} " ]]; then
    # whatever you want to do when array contains value
fi

if [[ ! " ${array[@]} " =~ " ${value} " ]]; then
    # whatever you want to do when array doesn't contain value
fi

検索する値がスペースのある配列要素内の単語の1つである場合、誤検知が発生することに注意してください。例えば

array=("Jack Brown")
value="Jack"

正規表現は、「Jack」が配列に含まれていない場合でも配列に含まれていると見なします。したがってIFS、次のようにこのソリューションを引き続き使用する場合は、正規表現の区切り文字を変更する必要があります

IFS=$'\t'
array=("Jack Brown\tJack Smith")
unset IFS
value="Jack"

if [[ "\t${array[@]}\t" =~ "\t${value}\t" ]]; then
    echo "true"
else
    echo "false"
fi

これは「false」を出力します。

もちろんこれはテストステートメントとしても使用でき、ワンライナーとして表現できます。

[[ " ${array[@]} " =~ " ${value} " ]] && echo "true" || echo "false"

1
最初の正規表現の値の一致の先頭にスペースを追加しました。これは、単語で終わるものではなく、単語のみに一致するようにするためです。よく働く。しかし、なぜ2番目の条件を使用するのか理解できません。最初の条件だけではうまくいきませんか?
JStrahl 2013年

1
@AwQiruiGuoフォローしているかわかりません。ドルリテラルを含む配列について話していますか?その場合は、バックスラッシュで照合する値のドルをエスケープしてください。
キーガン

10
[[ " ${branches[@]} " =~ " ${value} " ]] && echo "YES" || echo "NO";
Oneliner

3
Shellcheckは、このソリューションSC2199およびSC2076について不満を述べています。機能を壊さずに警告を修正することはできませんでした。その行のシェルチェックを無効にする以外にそれについて何か考えはありますか?
Ali Essam

4
SC2076は修正が簡単ifです。で二重引用符を削除するだけです。このアプローチでSC2199を回避する方法はないと思います。他のソリューションのいくつかに示されているように、配列を明示的にループするか、警告を無視する必要があります。
キーガン

388

以下は、これを実現するための小さな関数です。検索文字列は最初の引数で、残りは配列要素です。

containsElement () {
  local e match="$1"
  shift
  for e; do [[ "$e" == "$match" ]] && return 0; done
  return 1
}

その関数のテスト実行は次のようになります。

$ array=("something to search for" "a string" "test2000")
$ containsElement "a string" "${array[@]}"
$ echo $?
0
$ containsElement "blaha" "${array[@]}"
$ echo $?
1

5
うまくいきます!引用符のように配列を渡すことを忘れないでください: "${array[@]}"。そうしないと、スペースを含む要素が機能しなくなります。
ジューブ

26
いいね。最初の引数が2番目の引数に含まれているかどうかをチェックするため、これをelementIn()と呼びます。containsElements()は、配列が最初に表示されるように聞こえます。私のような初心者にとっては、 "if"ステートメントでstdoutに書き込まない関数を使用する方法の例が役立ちます:if elementIn "$table" "${skip_tables[@]}" ; then echo skipping table: ${table}; fi; ご協力ありがとうございます!
GlenPeterson 2013

5
@Bluz &&構文はブールAND演算子です。ブール演算子を使用すると、ブール文が作成されます。ブール論理では、&&の前後の両方のステートメントがtrueと評価された場合にのみ、ステートメント全体がtrueになることができます。これは、ifブロックに組み込まれたショートカットとして使用されます。テストが評価され、falseの場合、テストが失敗して実行されない場合は、ステートメント全体とは関係がないため、戻り値を評価する必要はありません。テストが成功した場合、ブール文の成功には、コードが実行されるように戻りの結果を決定する必要があります。
2013年

4
@James慣例により、bashの成功コードは「0」で、エラーはすべて> = 1です。これが成功時に0を返す理由です。:)
tftd

11
@Stelios shiftは、引数リストを1つ左にシフトし(最初の引数を削除)forin暗黙的に引数リストを反復処理しません。
クリスチャン

58
$ myarray=(one two three)
$ case "${myarray[@]}" in  *"two"*) echo "found" ;; esac
found

69
これは、配列内の各要素を個別に反復しないことに注意してください...代わりに、単に配列を連結し、「2」を部分文字列として照合します。これは、「2」という正確な単語が配列内の要素であるかどうかをテストしている場合に、望ましくない動作を引き起こす可能性があります。
MartyMacGyver 2013

私はこれがファイルの種類を比較する上でうまくいくと思っていましたが、カウンターが増えるにつれて、あまりにも多くの値をカウントしていることがわかりました...ブー!
マイクQ

17
違う!理由:case "${myarray[@]}" in *"t"*) echo "found" ;; esac出力:found
Sergej Jevsejev

@MartyMacGyverは、あなたがこの回答に私のほかに見てください可能性stackoverflow.com/a/52414872/1619950
アレクサンドルPodkutin

45
for i in "${array[@]}"
do
    if [ "$i" -eq "$yourValue" ] ; then
        echo "Found"
    fi
done

文字列の場合:

for i in "${array[@]}"
do
    if [ "$i" == "$yourValue" ] ; then
        echo "Found"
    fi
done

つまり、インデックス付きforループを使用して、配列要素にIFSが含まれている場合に
強制終了するの

@Matt:${#}Bashはスパース配列をサポートしているため、使用には注意が必要です。
追って通知があるまで一時停止。

@Paolo、配列にスペースが含まれている場合は、それを文字列として比較します。スペースも文字列です。
スコット

@Paolo:関数にすることはできますが、配列を引数として渡すことはできないので、グローバルとして扱う必要があります。
追って通知があるまで一時停止。

デニスは正しいです。bashリファレンスマニュアルから:「単語が二重引用符で囲まれている場合、... $ {name [@]}はnameの各要素を個別の単語に展開します」
mkb

37

ワンラインソリューション

printf '%s\n' ${myarray[@]} | grep -P '^mypattern$'

説明

このprintfステートメントは、配列の各要素を別々の行に出力します。

このgrepステートメントは、特殊文字^を使用して、(それ以上でも以下でもない)として指定されたパターンを正確$含む行を検索します。mypattern


使用法

これをif ... thenステートメントに入れるには:

if printf '%s\n' ${myarray[@]} | grep -q -P '^mypattern$'; then
    # ...
fi

追加した -qgrep一致を出力しないように式にフラグ。一致の存在を「真」として扱うだけです。


素敵な解決策!GNU grepには、パターンの「-P」と^および$を置き換える「--line-regexp」もあります。printf '%s \ n' $ {myarray [@]} | grep -q --line-regexp 'mypattern'
presto8 '29

19

パフォーマンスが必要な場合は、検索するたびに配列全体をループする必要はありません。

この場合、その配列のインデックスを表す連想配列(ハッシュテーブル、またはディクショナリ)を作成できます。つまり、各配列要素を配列内のインデックスにマップします。

make_index () {
  local index_name=$1
  shift
  local -a value_array=("$@")
  local i
  # -A means associative array, -g means create a global variable:
  declare -g -A ${index_name}
  for i in "${!value_array[@]}"; do
    eval ${index_name}["${value_array[$i]}"]=$i
  done
}

その後、次のように使用できます。

myarray=('a a' 'b b' 'c c')
make_index myarray_index "${myarray[@]}"

そして、そのようにメンバーシップをテストします:

member="b b"
# the "|| echo NOT FOUND" below is needed if you're using "set -e"
test "${myarray_index[$member]}" && echo FOUND || echo NOT FOUND

または:

if [ "${myarray_index[$member]}" ]; then 
  echo FOUND
fi

テストされた値または配列の値にスペースがある場合でも、このソリューションは正しいことを行うことに注意してください。

おまけとして、配列内の値のインデックスも取得します。

echo "<< ${myarray_index[$member]} >> is the index of $member"

連想配列を使用する必要があるという考えの+1。のコードmake_indexは、間接的なため、もう少し工夫されていると思います。はるかに単純なコードで固定配列名を使用することもできます。
ムシフィル2015

17

私は通常、次のように使用します:

inarray=$(echo ${haystack[@]} | grep -o "needle" | wc -w)

ゼロ以外の値は、一致が見つかったことを示します。


確かに、これは間違いなく最も簡単な解決策です-私の意見では答えをマークする必要があります。少なくとも私の賛成票を持っています![:
ToVine、2015

2
これは、同様の針では機能しません。例:haystack=(needle1 needle2); echo ${haystack[@]} | grep -o "needle" | wc -w
キーガン

1
とても本当です。どの要素にも存在しない区切り文字で結合し、それを針に追加すると、その助けになります。多分…(inarray=$(printf ",%s" "${haystack[@]}") | grep -o ",needle" | wc -w)
試聴なし

2
偽陽性を避けるだろうグレップ-x使用: inarray=$(printf ",%s" "${haystack[@]}") | grep -x "needle" | wc -l
jesjimher

おそらく単純inarray=$(echo " ${haystack[@]}" | grep -o " needle" | wc -w)に-xを指定すると、grepは入力文字列全体を照合します
MI Wright

17

関数のないもう1つのライナー:

(for e in "${array[@]}"; do [[ "$e" == "searched_item" ]] && exit 0; done) && echo "found" || echo "not found"

スペースに関するご協力ありがとうございます@Qwerty!

対応する機能:

find_in_array() {
  local word=$1
  shift
  for e in "$@"; do [[ "$e" == "$word" ]] && return 0; done
  return 1
}

例:

some_words=( these are some words )
find_in_array word "${some_words[@]}" || echo "expected missing! since words != word"

1
なぜここにサブシェルが必要なのですか?
codeforester 2018年

1
@codeforesterこれは古い...ですが、書かれているので、それを打破するためにそれが必要です。それはそれが行うことですexit 0(見つかった場合はできるだけ停止します)。
エスタニ2018年

1ライナーの終わりがあるべき|| echo not foundの代わりに、|| not foundまたはシェルは、の名前でコマンドを実行しようとしない引数で見つかった要求された値が配列ではない場合。
18

11
containsElement () { for e in "${@:2}"; do [[ "$e" = "$1" ]] && return 0; done; return 1; }

正しく空の配列を処理します。


これは@patrikの回答とどう違うのですか?私が目にする唯一の違いは、バグのように見える"$e" = "$1"(の代わりに"$e" == "$1")です。
CivFan 2015

1
そうではない。@ patrik'sは、私のコメントを彼の元の回答にマージしました(パッチ#4)。注:"e" == "$1"構文的に明確です。
Yann

@CivFan現在の形式では、エレガントな$ {@:2}と自己文書化$ 1のため、これはpatrikの回答の形式よりも短くなっています。[[]]内での引用は不要であると付け加えておきます。
Hontváriレベンテ

9

ここに小さな貢献があります:

array=(word "two words" words)  
search_string="two"  
match=$(echo "${array[@]:0}" | grep -o $search_string)  
[[ ! -z $match ]] && echo "found !"  

注:この方法では大文字と小文字の「2つの単語」は区別されませんが、質問では必須ではありません。


これは私を大いに助けました。ありがとう!
Ed Manet

質問は、あなたが正しい答えを与える必要があることを明確に述べていませんでしたが、私はそれが質問で暗黙的であると思います...配列には値「2」が含まれていません。
鉄人2017年

上記は「rd」の一致を報告します。
ノエルヤップ

6

正確で一致させるために配列全体を反復処理する価値があるかどうかを確認するためにすばやくダーティなテストを行う場合、Bashは配列をスカラーのように扱うことができます。スカラーで一致をテストし、一致がない場合はループをスキップすると時間を節約できます。明らかに、誤検知が発生する可能性があります。

array=(word "two words" words)
if [[ ${array[@]} =~ words ]]
then
    echo "Checking"
    for element in "${array[@]}"
    do
        if [[ $element == "words" ]]
        then
            echo "Match"
        fi
    done
fi

これは「チェック」と「マッチ」を出力します。これによりarray=(word "two words" something)、「チェック」のみが出力されます。array=(word "two widgets" something)何も出力されません。


文字列全体にのみ一致wordsする正規表現に置き換える^words$だけで、各項目を個別にチェックする必要が完全になくなるのはなぜですか。
Dejay Clayton 2017

@DejayClayton:まるでスカラーであるかのように配列全体を一度にpattern='^words$'; if [[ ${array[@]} =~ $pattern ]]チェックするため、決して一致しません。私の回答の個々のチェックは、大まかな一致に基づいて続行する理由がある場合にのみ行われます。
追って通知があるまで一時停止。

ああ、あなたが何をしようとしているのか分かります。より高性能で安全な別の答えを提案しました。
Dejay Clayton 2017

6

これは私のために働いています:

# traditional system call return values-- used in an `if`, this will be true when returning 0. Very Odd.
contains () {
    # odd syntax here for passing array parameters: http://stackoverflow.com/questions/8082947/how-to-pass-an-array-to-a-bash-function
    local list=$1[@]
    local elem=$2

    # echo "list" ${!list}
    # echo "elem" $elem

    for i in "${!list}"
    do
        # echo "Checking to see if" "$i" "is the same as" "${elem}"
        if [ "$i" == "${elem}" ] ; then
            # echo "$i" "was the same as" "${elem}"
            return 0
        fi
    done

    # echo "Could not find element"
    return 1
}

呼び出し例:

arr=("abc" "xyz" "123")
if contains arr "abcx"; then
    echo "Yes"
else
    echo "No"
fi

5
a=(b c d)

if printf '%s\0' "${a[@]}" | grep -Fqxz c
then
  echo 'array “a” contains value “c”'
fi

同等の長いオプションを使用したい場合:

--fixed-strings --quiet --line-regexp --null-data

1
--null-dataがないため、これはMacのBSD-grepでは機能しません。:(
ウィル

4

借入デニス・ウィリアムソンさんの回答の必要性を回避するには、以下のソリューションのコンバインアレイ、シェルセーフ引用、および正規表現:ループを反復処理します。パイプまたはその他のサブプロセスを使用します。または非bashユーティリティを使用します。

declare -a array=('hello, stack' one 'two words' words last)
printf -v array_str -- ',,%q' "${array[@]}"

if [[ "${array_str},," =~ ,,words,, ]]
then
   echo 'Matches'
else
   echo "Doesn't match"
fi

上記のコードは、Bash正規表現を使用して、配列の内容の文字列化されたバージョンと照合することで機能します。正規表現の一致が配列内の値の巧妙な組み合わせによってだまされないようにするための6つの重要なステップがあります。

  1. Bashの組み込みprintfシェル引用符を使用して、比較文字列を作成し%qます。シェル引用は、バックスラッシュでエスケープすることにより、特殊文字が「シェルセーフ」になることを保証します\
  2. 値の区切り文字として機能する特殊文字を選択します。区切り文字は、を使用するとエスケープされる特殊文字の1つである必要があり%qます。これは、配列内の値が正規表現の一致をだますための巧妙な方法で構築できないことを保証する唯一の方法です。カンマを選びます,は、その文字が、予期しない方法で評価または誤用された場合に最も安全であるためです。
  3. 区切り文字として機能する特殊文字の2つのインスタンスを使用して、すべての配列要素を1つの文字列に結合します。例としてカンマを使用し,,%qて、の引数として使用しましたprintf。特殊文字の2つのインスタンスは、区切り文字として表示される場合にのみ隣接して表示できるため、これは重要です。特殊文字の他のすべてのインスタンスはエスケープされます。
  4. 配列の最後の要素との照合を可能にするために、デリミタの末尾の2つのインスタンスを文字列に追加します。したがって、と比較する代わりに${array_str}、と比較し${array_str},,ます。
  5. 検索するターゲット文字列がユーザー変数によって提供されている場合は、特殊文字のすべてのインスタンスをバックスラッシュでエスケープする必要あります。そうしないと、正規表現の一致が巧妙に作成された配列要素に騙されやすくなります。
  6. 文字列に対してBash正規表現一致を実行します。

非常に賢い。ほとんどの潜在的な問題が回避されていることがわかりますが、コーナーケースがないかどうかをテストしたいと思います。また、ポイント5の処理の例を見たいと思いprintf -v pattern ',,%q,,' "$user_input"; if [[ "${array_str},," =~ $pattern ]]ます。
追って通知があるまで一時停止。

case "$(printf ,,%q "${haystack[@]}"),," in (*"$(printf ,,%q,, "$needle")"*) true;; (*) false;; esac
Tino

3

caseロジックを使用して配列に特定の値が含まれていることを確認することに関する@ ghostdog74の回答への小さな追加:

myarray=(one two three)
word=two
case "${myarray[@]}" in  ("$word "*|*" $word "*|*" $word") echo "found" ;; esac

または、extglobオプションをオンにして、次のようにすることができます:

myarray=(one two three)
word=two
shopt -s extglob
case "${myarray[@]}" in ?(*" ")"$word"?(" "*)) echo "found" ;; esac

また、ifステートメントでそれを行うことができます:

myarray=(one two three)
word=two
if [[ $(printf "_[%s]_" "${myarray[@]}") =~ .*_\[$word\]_.* ]]; then echo "found"; fi

2

与えられた:

array=("something to search for" "a string" "test2000")
elem="a string"

次に簡単なチェック:

if c=$'\x1E' && p="${c}${elem} ${c}" && [[ ! "${array[@]/#/${c}} ${c}" =~ $p ]]; then
  echo "$elem exists in array"
fi

どこ

c is element separator
p is regex pattern

([[]]内で直接式を使用するのではなく、pを個別に割り当てる理由は、bash 4の互換性を維持するためです)


😂...ここに「シンプル」という言葉の使用を愛する
キリスト教の

2

ここで紹介するアイデアのいくつかを組み合わせると、完全に単語が一致するループのないエレガントなifステートメントを作成できます。

$find="myword"
$array=(value1 value2 myword)
if [[ ! -z $(printf '%s\n' "${array[@]}" | grep -w $find) ]]; then
  echo "Array contains myword";
fi

これはwordまたはvalでトリガーされず、単語全体が一致するだけです。各配列の値に複数の単語が含まれている場合は壊れます。


1

私は通常、主にbashが参照によって変数を渡せないため、変数の値ではなく変数の名前を操作するために、この種のユーティリティを作成します。

配列の名前で機能するバージョンは次のとおりです。

function array_contains # array value
{
    [[ -n "$1" && -n "$2" ]] || {
        echo "usage: array_contains <array> <value>"
        echo "Returns 0 if array contains value, 1 otherwise"
        return 2
    }

    eval 'local values=("${'$1'[@]}")'

    local element
    for element in "${values[@]}"; do
        [[ "$element" == "$2" ]] && return 0
    done
    return 1
}

これにより、質問の例は次のようになります。

array_contains A "one" && echo "contains one"


誰かがif内で使用されているこの例を投稿できますか、特に配列でどのように渡すか。スクリプトへの引数がparamsを配列として扱うことによって渡されたかどうかを確認しようとしていますが、動作しません。params =( "$ @")check = array_contains $ {params} 'SKIPDIRCHECK' if [[$ {check} == 1]]; その後...。しかし、「asas」を引数としてスクリプトを実行すると、asas:コマンドが見つかりませんと言い続けます。:/
スティーブチャイルズ2016年

1

使用するgrepと、printf

各配列メンバーを新しい行にフォーマットしてからgrep、行をフォーマットします。

if printf '%s\n' "${array[@]}" | grep -x -q "search string"; then echo true; else echo false; fi
例:
$ array=("word", "two words")
$ if printf '%s\n' "${array[@]}" | grep -x -q "two words"; then echo true; else echo false; fi
true

これは、デリミタとスペースには問題がないことに注意してください。


1

「grep」とループのない1行のチェック

if ( dlm=$'\x1F' ; IFS="$dlm" ; [[ "$dlm${array[*]}$dlm" == *"$dlm${item}$dlm"* ]] ) ; then
  echo "array contains '$item'"
else
  echo "array does not contain '$item'"
fi

このアプローチでは、grepやループなどの外部ユーティリティを使用しません。

ここで何が起こるかです:

  • ワイルドカードサブストリングマッチャーを使用して、文字列に連結された配列内のアイテムを検索します。
  • 検索項目を1組の区切り文字で囲むことにより、誤検出の可能性を排除します。
  • 安全のため、印刷できない文字を区切り文字として使用しています。
  • の一時的な置き換えによって、配列の連結にも使用される区切り文字を実現します IFS変数値をます。
  • IFSサブシェル(括弧のペアの内側)で条件式を評価することにより、この値の置換を一時的にします。

DLMを排除します。IFSを直接使用します。
ロビンA.ミード

これが最良の答えです。とても気に入ったので、このテクニックを使って関数を書きました
ロビンA.ミード

1

パラメータ拡張の使用:

$ {parameter:+ word} parameterがnullまたは未設定の場合、何も置換されません。それ以外の場合は、wordの展開が置換されます。

declare -A myarray
myarray[hello]="world"

for i in hello goodbye 123
do
  if [ ${myarray[$i]:+_} ]
  then
    echo ${!myarray[$i]} ${myarray[$i]} 
  else
    printf "there is no %s\n" $i
  fi
done

${myarray[hello]:+_}連想配列には適していますが、通常のインデックス付き配列には適していません。問題は、連想配列のキーが存在するかどうかをチェックするのではなく、アレイの値を見つけることです。
エリック

0

回答した後、特に気に入った別の回答を読みましたが、問題があり、反対票が投じられました。私はインスピレーションを得て、ここに私が実行可能だと思う2つの新しいアプローチがあります。

array=("word" "two words") # let's look for "two words"

使用するgrepprintf

(printf '%s\n' "${array[@]}" | grep -x -q "two words") && <run_your_if_found_command_here>

を使用してfor

(for e in "${array[@]}"; do [[ "$e" == "two words" ]] && exit 0; done; exit 1) && <run_your_if_found_command_here>

not_found結果を追加 || <run_your_if_notfound_command_here>


0

これが私の見解です。

実行に時間がかかるため、回避できる場合はbash forループを使用しないでください。何かがループする必要がある場合は、それをシェルスクリプトよりも低レベルの言語で記述したものにします。

function array_contains { # arrayname value
  local -A _arr=()
  local IFS=
  eval _arr=( $(eval printf '[%q]="1"\ ' "\${$1[@]}") )
  return $(( 1 - 0${_arr[$2]} ))
}

これ_arrは、インデックスが入力配列の値から導出される一時的な連想配列を作成することで機能します。(連想配列はbash 4以降で使用できるため、この関数は以前のバージョンのbashでは機能しません。)$IFS空白での単語分割を回避するように設定しました。

関数には明示的なループが含まれていませんが、内部的にbashは入力するために入力配列をステップ実行しますprintf。printf形式は%q、入力データがエスケープされ、配列キーとして安全に使用できるようにするために使用します。

$ a=("one two" three four)
$ array_contains a three && echo BOOYA
BOOYA
$ array_contains a two && echo FAIL
$

この関数が使用するものはすべてbashに組み込まれているため、コマンド展開でも、外部のパイプが下にドラッグすることはありません。

そして、eval... を使いたくない場合は、別のアプローチを自由に使用できます。:-)


配列に角括弧が含まれている場合はどうなりますか?
gniourf_gniourf 2017

@gniourf_gniourf-角括弧がバランスしている場合は問題ないようですが、配列に角括弧がバランスしていない値が含まれている場合は問題があることがわかります。その場合eval、答えの最後に命令を呼び出します。:)
ghoti 2017

それは私が好きではないことではありませんeval(私はそれに反対することは何もありません。泣くほとんどの人とは異なりevalほとんどがそれについて何が悪いのか理解していないので)。コマンドが壊れているだけです。多分%q代わりの%s方が良いでしょう。
gniourf_gniourf 2017

1
@gniourf_gniourf:私は「別のアプローチ」ビットを意味するだけでした(そして、私はeval明らかに完全にあなたと一緒にいます)が、%q私が見ることができる他のものを壊すことなく、あなたは完全に正しいです。(%qが角かっこもエスケープすることを理解していませんでした。)私が確認して修正した別の問題は、空白文字に関するものでした。でa=(one "two " three)、キーガンの問題と同様に:array_contains a "two "偽陰性になっただけでなくarray_contains a two、偽陽性になった。設定するだけで簡単に修正できますIFS
ghoti 2017

空白に関しては、引用符が欠落しているためではないですか?また、グロブ文字で壊れます。代わりにこれが必要だと思います: eval _arr=( $(eval printf '[%q]="1"\ ' "\"\${$1[@]}\"") )、そしてを捨てることができますlocal IFS=。連想配列に空のキーを作成することをBashが拒否するため、配列の空のフィールドにはまだ問題があります。それを修正するための簡単なハック方法は、ダミー文字を付加することで、言うxeval _arr=( $(eval printf '[x%q]="1"\ ' "\"\${$1[@]}\"") )return $(( 1 - 0${_arr[x$2]} ))
gniourf_gniourf 2017

-1

すでに提案されている正規表現手法の私のバージョン:

values=(foo bar)
requestedValue=bar

requestedValue=${requestedValue##[[:space:]]}
requestedValue=${requestedValue%%[[:space:]]}
[[ "${values[@]/#/X-}" =~ "X-${requestedValue}" ]] || echo "Unsupported value"

ここで起こっていることは、サポートされている値の配列全体を単語に拡張し、特定の文字列(この場合は "X-")をそれぞれの前に付加し、要求された値に対して同じことを行っていることです。これが実際に配列に含まれている場合、結果の文字列は結果のトークンの1つと最大で一致するか、まったく一致しません。後者の場合、|| 演算子がトリガーされ、サポートされていない値を処理していることがわかります。そのすべての前に、要求された値は、標準のシェル文字列操作によってすべての先頭と末尾の空白が取り除かれます。

クリーンでエレガントだと思いますが、サポートされている値の配列が特に大きい場合のパフォーマンスがどれほどかはわかりません。


-1

これがこの問題に対する私の見解です。ここに短いバージョンがあります:

function arrayContains() {
        local haystack=${!1}
        local needle="$2"
        printf "%s\n" ${haystack[@]} | grep -q "^$needle$"
}

そして、長いバージョンは、目に優しいと思います。

# With added utility function.
function arrayToLines() {
        local array=${!1}
        printf "%s\n" ${array[@]}
}

function arrayContains() {
        local haystack=${!1}
        local needle="$2"
        arrayToLines haystack[@] | grep -q "^$needle$"
}

例:

test_arr=("hello" "world")
arrayContains test_arr[@] hello; # True
arrayContains test_arr[@] world; # True
arrayContains test_arr[@] "hello world"; # False
arrayContains test_arr[@] "hell"; # False
arrayContains test_arr[@] ""; # False

私は長い間bashを使用していなかったので、私は答えを理解するのに苦労したり、自分で書いたものでさえも:)この質問がまだずっと続いているとは信じられません:)
Paolo Tedesco

どうtest_arr=("hello" "world" "two words")ですか?
Qwerty、2016年

-1

別のスクリプト/コマンドによって生成されたIDのリストにIDが含まれているかどうかを確認しなければならない場合がありました。私にとっては次のように働きました:

# the ID I was looking for
ID=1

# somehow generated list of IDs
LIST=$( <some script that generates lines with IDs> )
# list is curiously concatenated with a single space character
LIST=" $LIST "

# grep for exact match, boundaries are marked as space
# would therefore not reliably work for values containing a space
# return the count with "-c"
ISIN=$(echo $LIST | grep -F " $ID " -c)

# do your check (e. g. 0 for nothing found, everything greater than 0 means found)
if [ ISIN -eq 0 ]; then
    echo "not found"
fi
# etc.

次のように短縮/圧縮することもできます:

if [ $(echo " $( <script call> ) " | grep -F " $ID " -c) -eq 0 ]; then
    echo "not found"
fi

私の場合、jqを実行してIDのリストの一部のJSONをフィルター処理していて、後で自分のIDがこのリストにあるかどうかを確認する必要があり、これが私にとって最善の方法でした。手動で作成したタイプの配列では機能しませんLIST=("1" "2" "4")が、改行で区切られたスクリプト出力では機能します。


PS .:私は比較的新しいので答えをコメントできませんでした...


-2

次のコードは、指定された値が配列内にあるかどうかを確認し、そのゼロベースのオフセットを返します。

A=("one" "two" "three four")
VALUE="two"

if [[ "$(declare -p A)" =~ '['([0-9]+)']="'$VALUE'"' ]];then
  echo "Found $VALUE at offset ${BASH_REMATCH[1]}"
else
  echo "Couldn't find $VALUE"
fi

一致は完全な値で行われるため、VALUE = "three"を設定すると一致しません。


-2

反復したくない場合は、調査する価値があります。

#!/bin/bash
myarray=("one" "two" "three");
wanted="two"
if `echo ${myarray[@]/"$wanted"/"WAS_FOUND"} | grep -q "WAS_FOUND" ` ; then
 echo "Value was found"
fi
exit

スニペットの原型:http ://www.thegeekstuff.com/2010/06/bash-array-tutorial/ かなり賢いと思います。

編集:あなたはおそらくただ行うことができます:

if `echo ${myarray[@]} | grep -q "$wanted"` ; then
echo "Value was found"
fi

ただし、後者は、配列に一意の値が含まれている場合にのみ機能します。"143"で1を検索すると、誤検出が発生します。


-2

少し遅れますが、これを使用できます:

#!/bin/bash
# isPicture.sh

FILE=$1
FNAME=$(basename "$FILE") # Filename, without directory
EXT="${FNAME##*.}" # Extension

FORMATS=(jpeg JPEG jpg JPG png PNG gif GIF svg SVG tiff TIFF)

NOEXT=( ${FORMATS[@]/$EXT} ) # Formats without the extension of the input file

# If it is a valid extension, then it should be removed from ${NOEXT},
#+making the lengths inequal.
if ! [ ${#NOEXT[@]} != ${#FORMATS[@]} ]; then
    echo "The extension '"$EXT"' is not a valid image extension."
    exit
fi

-2

これはzshでのみ機能することがわかりましたが、一般的なアプローチは良いと思います。

arr=( "hello world" "find me" "what?" )
if [[ "${arr[@]/#%find me/}" != "${arr[@]}" ]]; then
    echo "found!"
else
    echo "not found!"
fi

各要素からパターンを取り出すのは、それが開始${arr[@]/#pattern/}または終了${arr[@]/%pattern/}する場合のみです。これら2つの置換はbashで機能しますが、同時に両方とも機能します${arr[@]/#%pattern/} zshでのみ機能します。

変更された配列が元の配列と等しい場合、要素は含まれていません。

編集:

これはbashで動作します:

 function contains () {
        local arr=(${@:2})
        local el=$1
        local marr=(${arr[@]/#$el/})
        [[ "${#arr[@]}" != "${#marr[@]}" ]]
    }

置換後、両方の配列の長さを比較します。当然、配列に要素が含まれている場合、置換によって完全に削除され、カウントが異なります。

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