BASH'deki listede bir değişkenin olup olmadığını nasıl kontrol ederim?


138

Bir kullanıcı girişinin geçerliliğini kontrol bash bir komut dosyası yazmaya çalışıyorum.
Girdi (değişken diyelim x) geçerli değerler listesiyle eşleştirmek istiyorum .

şu anda ortaya çıkardığım şey:

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

Benim sorum, bunu yapmanın daha basit bir yolu olup olmadığı, çoğu programlama dili için
bir şey list.contains(x).

Ek:
Söyle listesi:

list="11 22 33"

listbir dize olarak değil, bir dizi olarak ele alındığından kodum yalnızca bu değerler için yankılanacaktır, 1başarısız olmasını isterken tüm dize manipülasyonları doğrulayacaktır .

Yanıtlar:


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

veya bir işlev oluşturun:

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

kullanmak için:

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

37
olmalı[[ $list =~ (^| )$x($| ) ]] && echo 'yes' || echo 'no'
Matvey Aksenov

12
Kullanıcı girişi normal ifade özel karakterleri içeriyorsa yanlış pozitif verebilir, örneğinx=.
glenn jackman

4
Bu, hiçbir yanlış pozitif / negatif verecektir: contains () { [[ "$1" =~ (^|[[:space:]])"$2"($|[[:space:]]) ]]; }.
skozin

6
daha özlü bir çözüm [[ " $list " =~ " $x " ]] && echo 'yes' || echo 'no'. alanın ayırıcı olduğu ve $xalan içermediği varsayıldığında doğrudur
Tianren Liu

2
Ben bir "var" fonksiyonunu kullanmak daha iyi olduğunu düşünüyorum, isIn()böylece ilk parametre öğelerde yazabilirsiniz. Ayrıca böyle echokullanmak yerine bir şey yapabilirsiniz exit: [[ $2 =~ (^|[[:space:]])$1($|[[:space:]]) ]] && echo 1 || echo 0Yani fonksiyonu bu şekilde kullanabilirsiniz:result=$(isIn "-t" "-o -t 45") && echo $result
hayj

34

Eşleştirme doğrudur, ancak $ x teklifinde bulunmalı ve herhangi bir "boşluk" (örneğin yeni satır)

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

yani yani

# 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
}

bitir o zaman

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

veya

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

""Değişkenlerde kullanmanız gerektiğini unutmayın :

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

3
Bu çözüm $item, gibi özel karakterleri içeriyor olsa bile işe yarar .(ancak $listdeğişkenin testin içinde alıntılanması gerekir). Ve işlevi daha da basit tanımlanabilir: contains () { [[ "$1" =~ (^|[[:space:]])"$2"($|[[:space:]]) ]]; }.
skozin

gibi durumlarda çalışmaz: list="aa bb xx cc ff"andx="aa bb"
creativeChips

Merhaba. Ben bashscript temellerini öğrenmeye çalışıyorum ve nasıl liste, liste halinde ayrıştırabileceğini bilmek istedim ve o listede var kontrol yapmak. Uzay kullanarak bölündüğünüzü anlıyorum ama ifadeyi tam olarak anlayamadım. Google'a bana bu ifadenin temellerini kelime sağlayabilir: $list =~ (^|[[:space:]])"$item"($|[[:space:]]). Ya da, zamanınız varsa, bu ifade için açıklamanızı duymaktan memnuniyet duyarım. Not: Sanırım bir regex ifade (^ vb ile başlar) ama ne = ~ anlamını bilmiyorum. Genel bir açıklamayı çok isterim: P
Ali Yılmaz

28

IMHO'nun en kolay çözümü, orijinal dizeyi boşluk eklemek ve eklemek ve normal ifadeye karşı kontrol etmektir. [[ ]]

haystack='foo bar'
needle='bar'

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

bu, iğneyi alt dize olarak içeren değerlere sahip değerlerde yanlış pozitif olmayacaktır, örneğin samanlıkta foo barbaz.

(Konsept utanmadan çalınmış JQuery's hasClass()-Method)


4
ayırıcı boşluk değilse, çözüm daha açıktır. regex olmadan da yapılabilir: haystack="foo:bar"ve[[ ":$haystack:" = *:$needle:* ]]
phiphi

26

ne dersin

echo $list | grep -w $x

$?kararı vermek için çıktıyı veya yukarıdaki satırı kontrol edebilirsiniz .

grep -w tüm kelime kalıplarını kontrol eder.


1
"değer" geçerli ise (yani listede) alt dizesi olduğu için "val" değerini doğrulamaz
Ofir Farchy

Evet, kabul edilen cevap gibi. @glennjackman çalışan bir çözüm sunar.
f.ardelian

3
sessiz kalmak için grep -q kullanabilirsiniz
Amir

bu da kullanıcının istediği olmayabilir kısmi eşleşmeleri kabul eder
carnicer

3
@carnicer sonra tam bir eşleşme için sadece grep -w $ x kullanın
user1297406

18

Çift parantez kullanıyorsanız, (* joker karakterlerini) bir vaka ifadesinin dışında da kullanabilirsiniz:

string='My string';

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

3
Basit ve zarif, +1
João Pereira

14
"Benim" ifadesi başka bir dizenin bir alt dizesi ise yanlış pozitif verir, örneğin string = 'Komut Dizim'.
Luke Lee

Joker karakterlerin ( *My*) testin sağ tarafında olması gerektiğini belirtmek gerekir.
Jacob Vanus

8

Değer listeniz kod içinde sabit olarak kodlanacaksa, kullanımı test etmek oldukça basittir case. Gereksinimlerinize uyum sağlayabileceğiniz kısa bir örnek:

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

Liste çalışma zamanında bir dizi değişkeniyse, diğer yanıtlardan biri muhtemelen daha iyi bir seçimdir.


7

Liste komut dosyasında sabitse, aşağıdakileri en iyi şekilde beğenirim:

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

Ardından izin verilip verilmediğini validate "$x"test etmek için kullanın $x.

Tek astar istiyorsanız ve öğe adlarındaki boşlukla ilgilenmiyorsanız, bunu kullanabilirsiniz ( -wyerine not -x):

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

Notlar:

  • Bu POSIX shuyumludur.
  • validateyok değil alt dizeleri kabul (kaldırmak -xeğer istiyorum grep seçeneği).
  • validateargümanını normal bir ifade değil, sabit bir dize olarak yorumlar ( -Fisterseniz grep seçeneğini kaldırın ).

Fonksiyonu uygulamak için örnek kod:

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

6

İlişkilendirilebilir dizilerin anahtarlarından yararlanmayı düşünün . Ben profilli olmasa da, hem regex / desen eşleme ve döngü daha iyi performans varsayalım.

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

Çıktı:

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
... ve o değiştirme (ve güvenmeyin kolaylaştırabilirsiniz echo -nparametrelerin yaratıcı kullanımı ile): do is="${list[$value]+is }"; echo "$value ${is:-is *not* }a member of ( ${!list[*]} )"; done.
Toby Speight

"List =" bir iki üç xyz ... "gibi bir (uzun) listem varsa böyle bir dizi oluşturmanın kolay bir yolu var mı?
Ott Toomet

5

Formu echo $LIST | xargs -n1 echo | grep $VALUEaşağıda gösterildiği gibi kullanmanın daha kolay olduğunu düşünüyorum :

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

Bu, boşlukla ayrılmış bir liste için çalışır, ancak :aşağıdakileri yaparak listeyi diğer herhangi bir sınırlayıcıya (gibi ) uyarlayabilirsiniz :

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

"Testin çalışması için gerekli olduğunu unutmayın .


Bu neden olacaktır LIST="SOMEITEM1 ITEM2"olsa gerçek bile dönmek için ITEM1onun içinde değil
Ofir Farchy

İyi yakaladım, kısmi eşleşmeyi hariç tutmak için örneği grep -e ile güncelledim.
Sébastien Pierre

"İf" ifadesinin sonunda fazladan bir `` inanıyorum ''. Doğru form: [-n "` echo $ LIST | xargs -n1 echo | grep -e \ "^ $ VALUE $ \"]
Elad Tabak

3

Çözümümü listeye ekleyeceğimi düşündüm.

# 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

Örnekler

$ 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

listede

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 değişkeninin yalnızca 'binom' veya 'regresyon' olabileceğini varsayarsak, aşağıdakiler yapılır:

# 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

Listeye bir | ile ayırarak daha fazla dize ekleyebilirsiniz. karakter.

Egrep kullanmanın avantajı, büyük / küçük harfe duyarsızlık (-i) ekleyebilmeniz veya normal bir ifadeyle daha karmaşık senaryoları kontrol edebilmenizdir.


1

Bu neredeyse orijinal teklifiniz ama neredeyse 1 astarlı. Diğer geçerli cevaplar kadar karmaşık değil ve bash sürümlerine bağlı olarak değil (eski bashlarla çalışabilir).

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 ; }

Sanırım teklifim hem uzunluk hem de stil bakımından geliştirilebilir.


0

Çok uzun değilse; bunları mantıksal VEYA böyle bir karşılaştırma boyunca eşitlik arasında dizebilirsiniz.

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

Bu kesin problemim vardı ve yukarıdakiler çirkinken, diğer genel çözümlerden daha neler olduğu daha açık.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.