変数がBASHのリストに存在するかどうかを確認する方法


137

ユーザー入力の有効性をチェックするスクリプトをbashで記述しようとしています。
入力(たとえばvariable x)を有効な値のリストと照合したい。

私が現時点で思いついたのは:

for item in $list
do
    if [ "$x" == "$item" ]; then
        echo "In the list"
        exit
    fi
done

私の質問は、これを行う簡単な方法があるかどうかです。これは、ほとんどのプログラミング言語に
似てlist.contains(x)います。

追加:
リストは次のとおりです:

list="11 22 33"

私のコードはlist、文字列ではなく配列として扱われるため、これらの値についてのみメッセージをエコーし​​ます。すべての文字列操作は検証されますが1、失敗することになります。

回答:


142
[[ $list =~ (^|[[:space:]])$x($|[[:space:]]) ]] && echo 'yes' || echo 'no'

または関数を作成します。

contains() {
    [[ $1 =~ (^|[[:space:]])$2($|[[:space:]]) ]] && exit(0) || exit(1)
}

それを使用するには:

contains aList anItem
echo $? # 0: match, 1: failed

37
する必要があります[[ $list =~ (^| )$x($| ) ]] && echo 'yes' || echo 'no'
Matvey Aksenov

12
ユーザ入力は、たとえば、正規表現の特殊文字が含まれている場合は偽陽性を与える可能性x=.
グレンはジャックマン

4
これは偽陽性/陰性を与えません:contains () { [[ "$1" =~ (^|[[:space:]])"$2"($|[[:space:]]) ]]; }
スコジン2014年

6
より簡潔なソリューション:[[ " $list " =~ " $x " ]] && echo 'yes' || echo 'no'。スペースがセパレーターであり、スペースが$x含まれていないと仮定すると正しくなります
Tianren Liu

2
isIn()最初にパラメーターで項目を記述できるように、「is in」関数を使用する方が良いと思います。また、あなたのことができecho代わりに使用しての何かexit:次のように[[ $2 =~ (^|[[:space:]])$1($|[[:space:]]) ]] && echo 1 || echo 0:あなたは機能をこのように使用できるようにresult=$(isIn "-t" "-o -t 45") && echo $result
hayj

34

Matveyは正しいですが、$ xを引用して、あらゆる種類の「スペース」(たとえば、新しい行)を検討する必要があります。

[[ $list =~ (^|[[:space:]])"$x"($|[[:space:]]) ]] && echo 'yes' || echo 'no' 

そう、すなわち

# list_include_item "10 11 12" "2"
function list_include_item {
  local list="$1"
  local item="$2"
  if [[ $list =~ (^|[[:space:]])"$item"($|[[:space:]]) ]] ; then
    # yes, list include item
    result=0
  else
    result=1
  fi
  return $result
}

その時終わり

`list_include_item "10 11 12" "12"`  && echo "yes" || echo "no"

または

if `list_include_item "10 11 12" "1"` ; then
  echo "yes"
else 
  echo "no"
fi

""変数の場合に使用する必要があることに注意してください。

`list_include_item "$my_list" "$my_item"`  && echo "yes" || echo "no"

3
このソリューション$itemは、特殊文字が含まれている場合でも機能.$listます(ただし、変数はおそらくテスト内で引用する必要があります)。そして、関数はさらに簡単に定義できます:contains () { [[ "$1" =~ (^|[[:space:]])"$2"($|[[:space:]]) ]]; }
スコジン2014年

:のようなケースでは動作しないlist="aa bb xx cc ff"x="aa bb"
creativeChips

こんにちは。私はbashscriptの基礎を学びたいのですが、リストの文字列をリストに解析して、そのリストの存在チェックを行う方法を知りたいと思っていました。スペースを使って分割することは理解できますが、表現を完全には理解できませんでした。この表現の基礎をGoogleに提供してください$list =~ (^|[[:space:]])"$item"($|[[:space:]])。または、時間があれば、この表現について説明をいただければ幸いです。注:正規表現(^などで始まる)だと思いますが、=〜の意味がわかりません。だから私は全体的な説明が大好きです:P
アリYılmaz

28

IMHOの最も簡単な解決策は、元の文字列に先頭にスペースを追加して追加し、次の正規表現と照合することです。 [[ ]]

haystack='foo bar'
needle='bar'

if [[ " $haystack " =~ .*\ $needle\ .* ]]; then
    ...
fi

これは、たとえばhaystackのように、部分文字列として針を含む値を持つ値では、誤検知ではありませんfoo barbaz

(コンセプトは、JQueryのhasClass()-Methodから恥知らずに盗まれたものです)


4
セパレータがスペースでない場合、解決策はより明白です。正規表現なしでも実行できます: haystack="foo:bar"および[[ ":$haystack:" = *:$needle:* ]]
phiphi

25

いかがですか

echo $list | grep -w $x

出力または$?上記の行のいずれかを確認して、決定を下すことができます 。

grep -w 単語パターン全体をチェックします。


1
「値」が有効な場合(リストに含まれる場合)、そのサブストリングであるため、「val」は検証されません
Ofir Farchy

はい、受け入れられた回答と同様に、そうなります。@glennjackmanは実用的なソリューションを提供します。
f.ardelian 2013

3
grep -qを使用してgrepを静かにすることができます
Amir

これは、ユーザーが何を望んではないかもしれない部分一致、受け入れるだろう
carnicer

3
@carnicerは、次にgrep -w $ xを使用して完全に一致させます
user1297406

18

二重括弧を使用する場合は、caseステートメントの外でも(*ワイルドカード)を使用できます。

string='My string';

if [[ $string == *My* ]]
then
echo "It's there!";
fi

3
1、シンプルでエレガントな
ジョアン・ペレイラ

14
これは、 "My"が別の文字列の部分文字列である場合、たとえばstring = 'MyCommand string'の場合に誤検知を与えます。
ルーク・リー

ワイルドカード(*My*)がテストの右側にある必要があることに注意してください。
Jacob Vanus

8

値のリストをスクリプトでハードコーディングする場合、を使用してテストするのはかなり簡単caseです。これは、要件に適合できる短い例です。

for item in $list
do
    case "$x" in
      item1|item2)
        echo "In the list"
        ;;
      not_an_item)
        echo "Error" >&2
        exit 1
        ;;
    esac
done

実行時にリストが配列変数である場合、他の答えのいずれかがおそらくより適しています。


7

リストがスクリプトで修正されている場合、私は次のものが一番好きです。

validate() {
    grep -F -q -x "$1" <<EOF
item 1
item 2
item 3
EOF
}

次にvalidate "$x"$xが許可されているかどうかをテストするために使用します。

ワンライナーが必要で、アイテム名の空白を気にしない場合は、これを使用できます(の-w代わりに通知-x)。

validate() { echo "11 22 33" | grep -F -q -w "$1"; }

ノート:

  • これはPOSIXにsh準拠しています。
  • validate部分文字列を受け入れませ-x必要に応じてgrep のオプションを削除してください)。
  • validate引数を正規表現ではなく固定文字列として解釈します(-F必要に応じてgrep のオプションを削除してください)。

関数を実行するためのサンプルコード:

for x in "item 1" "item2" "item 3" "3" "*"; do
    echo -n "'$x' is "
    validate "$x" && echo "valid" || echo "invalid"
done

6

連想配列のキーを活用することを検討してください。私はこれをプロファイルしていませんが、これは正規表現/パターンマッチングとループの両方を上回っていると思います。

declare -A list=( [one]=1 [two]=two [three]='any non-empty value' )
for value in one two three four
do
    echo -n "$value is "
    # a missing key expands to the null string, 
    # and we've set each interesting key to a non-empty value
    [[ -z "${list[$value]}" ]] && echo -n '*not* '
    echo "a member of ( ${!list[*]} )"
done

出力:

one is a member of ( one two three )
two is a member of ( one two three )
three is a member of ( one two three )
four is *not* a member of ( one two three )

2
...そしてecho -n、パラメーターのクリエイティブな使用により、その置き換えを簡略化できます(依存しません)do is="${list[$value]+is }"; echo "$value ${is:-is *not* }a member of ( ${!list[*]} )"; done
Toby Speight 2016年

`list =" one two three xyz ... "のような(長い)リストしかない場合、そのような配列を作成する簡単な方法はありますか?
Ott Toomet 2018年

5

echo $LIST | xargs -n1 echo | grep $VALUE以下に示すように、フォームを使用する方が簡単です。

LIST="ITEM1 ITEM2"
VALUE="ITEM1"
if [ -n "`echo $LIST | xargs -n1 echo | grep -e \"^$VALUE`$\" ]; then
    ...
fi

これはスペースで区切られたリストで機能しますが:、次のようにして他の区切り文字(など)に適合させることができます。

LIST="ITEM1:ITEM2"
VALUE="ITEM1"
if [ -n "`echo $LIST | sed 's|:|\\n|g' | grep -e \"^$VALUE`$\"`" ]; then
   ...
fi

"テストが機能するために必要なことに注意してください。


これは、その中LIST="SOMEITEM1 ITEM2"ITEM1ない場合でもtrueを返すことになります
Ofir Farchy

良いキャッチ、私は部分一致を除外するためにgrep -eで例を更新しました。
セバスチャンピエール

「if」ステートメントの最後に追加の `があると思います。正しい形式は次のとおりです:[-n "` echo $ LIST | xargs -n1 echo | grep -e \ "^ $ VALUE $ \"]
Elad Tabak

3

自分のソリューションをリストに追加すると思いました。

# Checks if element "$1" is in array "$2"
# @NOTE:
#   Be sure that array is passed in the form:
#       "${ARR[@]}"
elementIn () {
    # shopt -s nocasematch # Can be useful to disable case-matching
    local e
    for e in "${@:2}"; do [[ "$e" == "$1" ]] && return 0; done
    return 1
}

# Usage:
list=(11 22 33)
item=22

if elementIn "$item" "${list[@]}"; then
    echo TRUE;
else
    echo FALSE
fi
# TRUE

item=44
elementIn $item "${list[@]}" && echo TRUE || echo FALSE
# FALSE

1

$ in_list super test me out
NO

$ in_list "super dude" test me out
NO

$ in_list "super dude" test me "super dude"
YES

# How to use in another script
if [ $(in_list $1 OPTION1 OPTION2) == "NO" ]
then
  echo "UNKNOWN type for param 1: Should be OPTION1 or OPTION2"
  exit;
fi

in_list

function show_help()
{
  IT=$(CAT <<EOF

  usage: SEARCH_FOR {ITEM1} {ITEM2} {ITEM3} ...

  e.g. 

  a b c d                    -> NO
  a b a d                    -> YES
  "test me" how "test me"    -> YES

  )
  echo "$IT"
  exit
}

if [ "$1" == "help" ]
then
  show_help
fi

if [ "$#" -eq 0 ]; then
  show_help
fi

SEARCH_FOR=$1
shift;

for ITEM in "$@"
do
  if [ "$SEARCH_FOR" == "$ITEM" ]
  then
    echo "YES"
    exit;
  fi
done

echo "NO"

1

TARGET変数が 'binomial'または 'regression'のみであると仮定すると、次のようになります。

# Check for modeling types known to this script
if [ $( echo "${TARGET}" | egrep -c "^(binomial|regression)$" ) -eq 0 ]; then
    echo "This scoring program can only handle 'binomial' and 'regression' methods now." >&2
    usage
fi

|で区切ることにより、リストに文字列を追加できます。(パイプ)キャラクター。

egrepを使用する利点は、大文字と小文字を区別しない(-i)を追加したり、正規表現を使用してより複雑なシナリオをチェックしたりできることです。


1

これはほとんどあなたの元の提案ですが、ほぼ1ライナーです。他の有効な回答ほど複雑ではなく、bashのバージョンに依存することもありません(古いbasで機能します)。

OK=0 ; MP_FLAVOURS="vanilla lemon hazelnut straciatella"
for FLAV in $MP_FLAVOURS ; do [ $FLAV == $FLAVOR ] && { OK=1 ; break; } ; done
[ $OK -eq 0 ] && { echo "$FLAVOR not a valid value ($MP_FLAVOURS)" ; exit 1 ; }

私の提案は、長さとスタイルの両方で、まだ改善できると思います。


0

長すぎない場合; そのように、論理OR比較に沿って、等しいものの間でそれらを文字列化できます。

if [ $ITEM == "item1" -o $ITEM == "item2" -o $ITEM == "item3" ]; then
    echo In the list
fi 

私はこの正確な問題を抱えていて、上記は醜いですが、他の一般的な解決策よりも何が起こっているのかが明白です。

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