जांचें कि क्या बैश ऐरे में कोई मान है


443

बैश में, एक सरणी का एक निश्चित मान होता है, तो परीक्षण करने का सबसे सरल तरीका क्या है?

संपादित करें : उत्तर और टिप्पणियों की मदद से, कुछ परीक्षण के बाद, मैं इसके साथ आया:

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

ध्यान दें कि जिन मामलों में आप जो मूल्य खोज रहे हैं, वह रिक्त स्थान वाले सरणी तत्व में से एक शब्द है, यह गलत सकारात्मकता देगा। उदाहरण के लिए

array=("Jack Brown")
value="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

यह "गलत" छापेगा।

जाहिर तौर पर इसे टेस्ट स्टेटमेंट के रूप में भी इस्तेमाल किया जा सकता है, जिससे इसे वन-लाइनर के रूप में व्यक्त किया जा सकता है

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

1
मैंने पहले रेगेक्स मूल्य मैच की शुरुआत में एक स्थान जोड़ा, ताकि यह केवल शब्द से मेल खाए, न कि शब्द में कुछ समाप्त हो। बहुत अच्छा काम करता है। होवर, मुझे समझ में नहीं आता कि आप दूसरी शर्त का उपयोग क्यों करते हैं, क्या पहला काम अकेले ठीक नहीं होगा?
JStrahl

1
@AwQiruiGuo मुझे यकीन नहीं है कि मैं पीछा कर रहा हूँ। क्या आप डॉलर शाब्दिक के साथ सरणियों के बारे में बात कर रहे हैं? यदि हां, तो बैकस्लैश के साथ मेल खाते मूल्य से डॉलर बचाना सुनिश्चित करें।
कीगन

10
ऑनलाइनर: [[ " ${branches[@]} " =~ " ${value} " ]] && echo "YES" || echo "NO";
ericson.cepeda

3
शेलचेक इस समाधान, SC2199 और SC2076 के बारे में शिकायत करता है। मैं कार्यक्षमता को तोड़े बिना चेतावनियों को ठीक नहीं कर सकता था। उस लाइन के लिए शेलचेक को अक्षम करने के अलावा उस बारे में कोई विचार?
अली एसम

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
अच्छा लगा। मैं इसे तत्व कहूँगा () क्योंकि यह जाँचता है कि पहला तर्क दूसरे में निहित है या नहीं। युक्तियां () लगता है कि सरणी पहले जाएगी। मेरे जैसे newbies के लिए, एक फ़ंक्शन का उपयोग करने का एक उदाहरण जो "अगर" कथन में stdout को नहीं लिखता है तो मदद मिलेगी: if elementIn "$table" "${skip_tables[@]}" ; then echo skipping table: ${table}; fi; आपकी मदद के लिए धन्यवाद!
GlenPeterson

5
@Bluz && निर्माण एक बूलियन और ऑपरेटर है। बूलियन ऑपरेटरों का उपयोग एक बूलियन स्टेटमेंट बनाता है। बूलियन लॉजिक कहता है कि पूरा कथन केवल तभी सत्य हो सकता है जब && के बाद के दोनों कथन सत्य का मूल्यांकन करते हैं। इसका उपयोग एक शॉर्टकट के रूप में किया जाता है और यदि ब्लॉक किया जाता है। परीक्षण का मूल्यांकन किया जाता है और यदि गलत है, तो वापसी का मूल्यांकन करने की कोई आवश्यकता नहीं है क्योंकि यह परीक्षण विफल होने के बाद पूरे विवरण के लिए अप्रासंगिक है और इसलिए नहीं चलाया जाता है। यदि परीक्षण सफल होता है, तो बूलियन स्टेटमेंट की सक्सेज़ की आवश्यकता होती है, ताकि रिटर्न का परिणाम निर्धारित किया जा सके, इसलिए कोड चलाया जाता है।
पेटीएम

4
@Jash by bash में सफलता का कोड कन्वेंशन है "0" और त्रुटि सब कुछ है = = 1. यही कारण है कि यह सफलता पर 0 देता है। :)
tftd

11
@Stelios shiftतर्क सूची को बाईं ओर 1 से स्थानांतरित करता है (पहले तर्क को छोड़ता है) और तर्क सूची पर forएक inनिहितार्थ के बिना ।
क्रिश्चियन

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

69
ध्यान दें कि यह सरणी में प्रत्येक तत्व पर अलग से पुनरावृत्ति नहीं करता है ... इसके बजाय यह सरणी को संक्षिप्त करता है और एक विकल्प के रूप में "दो" से मेल खाता है। यह अवांछनीय व्यवहार का कारण बन सकता है अगर कोई परीक्षण कर रहा है कि क्या सटीक शब्द "दो" सरणी में एक तत्व है।
MartyMacGyver

मुझे लगा कि यह फ़ाइल प्रकारों की तुलना करने में मेरे लिए काम करने वाला था, लेकिन पाया कि जैसे-जैसे काउंटर बढ़ते गए, यह बहुत अधिक मूल्यों की गिनती कर रहा था ... बू!
माइक क्यू

17
गलत! कारण: case "${myarray[@]}" in *"t"*) echo "found" ;; esacआउटपुट:found
फ्रैंकफर्ट जेवसेज

@MartyMacGyver, क्या आप कृपया इस उत्तर के लिए मेरे जोड़ पर देख सकते हैं stackoverflow.com/a/52414872/1619950
Aleksandr 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

उस ने कहा, आप लूप के लिए अनुक्रमित का उपयोग कर सकते हैं और तब मारा जा सकता है जब किसी सरणी तत्व में IFS होता है: ((i = 0; मैं <$ {# सरणी [@]}; i ++))
mkb

@ मैट: आपको सावधान रहना होगा ${#}क्योंकि बाश विरल सरणियों का समर्थन करता है।
अगली सूचना तक रोक दिया गया।

@ पाओलो, अगर आपके एरे में स्पेस है तो इसे एक स्ट्रिंग के रूप में तुलना करें। एक स्पेस एक स्ट्रिंग भी है।
स्कॉट

@ पाओलो: आप इसे एक समारोह बना सकते हैं, लेकिन सरणियों को तर्कों के रूप में पारित नहीं किया जा सकता है, इसलिए आपको इसे वैश्विक मानना ​​होगा।
अगली सूचना तक रोक दिया गया।

डेनिस सही है। बैश संदर्भ मैनुअल से: "यदि शब्द डबल-कोटेड है, ... $ {नाम [@]} एक अलग शब्द के लिए नाम के प्रत्येक तत्व का विस्तार करता है"
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

मैंने एक -qध्वज जोड़ाgrep अभिव्यक्ति में ताकि यह मेल न खाए; यह सिर्फ एक मैच के अस्तित्व को "सच" मान लेगा।


अच्छा समाधान! GNU grep पर, "--line-regexp" भी है जो "-P" और ^ और $ को पैटर्न में बदल सकता है: Printf '% s \ n' $ {myarray [@]} | grep -q --line-regexp 'mypattern'
presto8

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है कि इनडायरेक्ट होने के कारण कोड थोड़ा अधिक वंचित है; आप एक बहुत सरल कोड के साथ एक निश्चित सरणी नाम का उपयोग कर सकते थे।
मुशीफिल

17

मैं आमतौर पर सिर्फ उपयोग करता हूं:

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

गैर शून्य मान इंगित करता है कि एक मैच मिला था।


सच है, यह निश्चित रूप से सबसे आसान समाधान है - मेरी राय में जवाब चिह्नित किया जाना चाहिए। कम से कम मेरा उत्थान हो! [:
टोविन २४'१५

2
इसी तरह की सुइयों के लिए काम नहीं करेगा। उदाहरण के लिए,haystack=(needle1 needle2); echo ${haystack[@]} | grep -o "needle" | wc -w
कीगन

1
सच सच। किसी भी तत्व में मौजूद नहीं एक सीमांकक के साथ जुड़ने और इसे सुई में जोड़ने से उस के साथ मदद मिलेगी। शायद कुछ इस तरह ... ( inarray=$(printf ",%s" "${haystack[@]}") | grep -o ",needle" | wc -w)
निष्कलंक

2
inarray=$(printf ",%s" "${haystack[@]}") | grep -x "needle" | wc -l
Grep

शायद के inarray=$(echo " ${haystack[@]}" | grep -o " needle" | wc -w)रूप में -x का कारण बनता है और पूरे इनपुट स्ट्रिंग मिलान करने के लिए grep का कारण बनता है
MI राइट

17

एक और एक लाइनर एक समारोह के बिना:

(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
हमें यहां एक उपधारा की आवश्यकता क्यों है?
कोडवर्डर

1
@codeforester यह पुराना है ... लेकिन जैसा कि लिखा गया था कि इसे तोड़ने के लिए आपको इसकी आवश्यकता है, यही वह है जो exit 0करता है (अगर मिला तो रोक देता है)।
एस्टनी

एक लाइनर के अंत होना चाहिए || echo not foundके बजाय || not foundया शेल के नाम से एक आदेश पर अमल करने की कोशिश करेंगे नहीं तर्क के साथ पाया यदि अनुरोध मान सरणी में नहीं है।
9

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

अब खाली सरणियों को सही ढंग से संभालता है।


यह @ पेट्रिक के उत्तर से कैसे भिन्न है? एकमात्र अंतर जो मुझे दिखाई देता है "$e" = "$1"(इसके बजाय "$e" == "$1") जो बग जैसा दिखता है।
CivFan

1
यह नहीं। @ पैट्रिक ने मेरी टिप्पणी को उसके मूल उत्तर में वापस मिला दिया (फिर पैच # 4)। नोट: "e" == "$1"वाक्यात्मक रूप से स्पष्ट है।
यान

@CivFan अपने वर्तमान स्वरूप में, यह $ $ $ $ @ @ @: 2 के कारण पेट्रीक के उत्तर में एक से कम है, और स्व दस्तावेज़ $ 1 है। मैं यह जोड़ना चाहूंगा कि [[]] के भीतर उद्धृत करना आवश्यक नहीं है।
होनतवेरी लेवेंटे

9

यहाँ एक छोटा सा योगदान है:

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

नोट: इस तरह मामला "दो शब्दों" में अंतर नहीं करता है लेकिन प्रश्न में इसकी आवश्यकता नहीं है।


इसने मेरी बहुत मदद की। धन्यवाद!
एड मानेट

सवाल स्पष्ट रूप से नहीं कहा गया था कि आपको सही उत्तर देना था लेकिन मुझे लगता है कि यह प्रश्न में निहित है ... सरणी में "दो" का मान नहीं है।
टेटसुजिन

उपरोक्त 'rd' के लिए एक मैच की रिपोर्ट करेगा।
नोएल याप

6

यदि आप एक त्वरित और गंदे परीक्षण करना चाहते हैं, तो यह देखने के लिए कि क्या सटीक मिलान प्राप्त करने के लिए पूरे सरणी पर चलने लायक है, बैश स्केल की तरह सरणियों का इलाज कर सकता है। स्केलर में एक मैच के लिए परीक्षण करें, यदि कोई नहीं है तो लूप को छोड़ना समय बचाता है। जाहिर है आपको झूठी सकारात्मकता मिल सकती है।

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$केवल पूरे स्ट्रिंग से मेल खाता है, जो प्रत्येक आइटम को व्यक्तिगत रूप से जांचने की आवश्यकता को पूरी तरह से समाप्त कर देता है?
देजय क्लेटन

@DejayClayton: क्योंकि यह एक स्केलर के रूप में एक ही बार में पूरे सरणी pattern='^words$'; if [[ ${array[@]} =~ $pattern ]]की जाँच कर रहा है, क्योंकि मैच कभी नहीं होगा । मेरे जवाब में व्यक्तिगत जाँच केवल तभी की जानी चाहिए जब किसी कारण से किसी मोटे मैच के आधार पर आगे बढ़ना हो।
अगली सूचना तक रोक दिया गया।

आह, मैं देख रहा हूँ कि तुम क्या करने की कोशिश कर रहे हो। मैंने एक वेरिएंट उत्तर का प्रस्ताव दिया है जो अधिक निष्पादन और सुरक्षित है।
देजय क्लेटन

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
यह मैक पर BSD-grep के साथ काम नहीं करता है, क्योंकि कोई भी डेटा नहीं है। :(
विल

4

डेनिस विलियमसन के उत्तर से उधार लेना , निम्नलिखित समाधान सरणियों, शेल-सुरक्षित उद्धरण और नियमित अभिव्यक्तियों को जोड़ती है ताकि आवश्यकता से बचने के लिए: छोरों पर चलना; पाइप या अन्य उप-प्रक्रियाओं का उपयोग करना; या गैर-बैश उपयोगिताओं का उपयोग करना।

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

उपरोक्त कोड सरणी सामग्री के कड़े संस्करण के विरुद्ध मिलान करने के लिए बैश नियमित अभिव्यक्तियों का उपयोग करके काम करता है। यह सुनिश्चित करने के लिए छह महत्वपूर्ण चरण हैं कि नियमित अभिव्यक्ति मैच को सरणी के भीतर मूल्यों के चतुर संयोजनों द्वारा मूर्ख नहीं बनाया जा सकता है:

  1. बैश के अंतर्निहित printfशेल-कोटिंग का उपयोग करके तुलना स्ट्रिंग का निर्माण करें %q। शेल-कोटिंग यह सुनिश्चित करेगा कि विशेष वर्ण बैकस्लैश से बचकर "शेल-सेफ" बन जाएं \
  2. मान सीमांकक के रूप में सेवा करने के लिए एक विशेष वर्ण चुनें। परिसीमनकर्ता विशेष पात्रों में से एक है जो उपयोग करते समय बच जाएगा %q; यह सुनिश्चित करने का एकमात्र तरीका है कि नियमित अभिव्यक्ति मैच को मूर्ख बनाने के लिए सरणी के भीतर मानों का निर्माण चतुर तरीकों से नहीं किया जा सकता है। मैं कॉमा चुनता हूं, क्योंकि यह चरित्र तब स्पष्ट होता है जब किसी अनपेक्षित रूप से इसका दुरुपयोग या दुरुपयोग होता है।
  3. सभी वर्ण तत्वों को एक ही स्ट्रिंग में संयोजित करें, विशेष वर्ण के दो उदाहरणों को सीमांकक के रूप में सेवा करने के लिए। एक उदाहरण के रूप में अल्पविराम का उपयोग करते हुए, मैंने ,,%qतर्क के रूप में उपयोग किया printf। यह महत्वपूर्ण है क्योंकि विशेष चरित्र के दो उदाहरण केवल एक दूसरे के बगल में दिखाई दे सकते हैं जब वे सीमांकक के रूप में दिखाई देते हैं; विशेष चरित्र के अन्य सभी उदाहरणों से बच जाएंगे।
  4. सरणी के अंतिम तत्व के खिलाफ मैचों की अनुमति देने के लिए, स्ट्रिंग के सीमांकक के दो अनुगामी उदाहरणों को संलग्न करें। इस प्रकार, तुलना करने के बजाय ${array_str}, तुलना करें ${array_str},,
  5. यदि आपके द्वारा खोजे जा रहे लक्ष्य स्ट्रिंग को एक उपयोगकर्ता चर द्वारा आपूर्ति की जाती है, तो आपको बैकस्लैश के साथ विशेष वर्ण के सभी उदाहरणों से बचना चाहिए । अन्यथा, नियमित रूप से अभिव्यक्ति मैच चतुराई से तैयार की गई सरणी तत्वों द्वारा मूर्ख बनने के लिए असुरक्षित हो जाता है।
  6. स्ट्रिंग के खिलाफ बैश नियमित अभिव्यक्ति मैच करें।

बहुत चालाक। मैं देख सकता हूं कि अधिकांश संभावित मुद्दों को रोका गया है, लेकिन मैं यह देखना चाहता हूं कि कोई कोने के मामले हैं या नहीं। इसके अलावा, मैं हैंडलिंग पॉइंट का एक उदाहरण देखना चाहूंगा printf -v pattern ',,%q,,' "$user_input"; if [[ "${array_str},," =~ $pattern ]]। 5. शायद कुछ ऐसा ।
अगली सूचना तक रोक दिया गया।

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

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

(सीधे [[]] बश 4 के लिए अनुकूलता बनाए रखने के लिए, अलग से पी असाइन करने का कारण [[]] है।


यहाँ "सरल" शब्द का उपयोग करें ...
ईसाई

2

यहां प्रस्तुत कुछ विचारों को मिलाकर आप एक सुंदर बना सकते हैं यदि बिना लूप के स्टेटमेंट जो सटीक शब्द मिलान करता है ।

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

यह wordया valकेवल पूरे शब्द मिलान पर ट्रिगर नहीं होगा । यदि प्रत्येक ऐरे मान में कई शब्द होंगे तो यह टूट जाएगा।


1

मैं आम तौर पर चर के मूल्य के बजाय चर के नाम पर काम करने के लिए इस तरह की उपयोगिताओं को लिखता हूं, मुख्य रूप से क्योंकि बैश अन्यथा संदर्भ द्वारा चर नहीं पारित कर सकते हैं।

यहाँ एक संस्करण है जो सरणी के नाम के साथ काम करता है:

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"

आदि।


क्या कोई इसका इस्तेमाल उदाहरण के लिए कर सकता है, यदि आप सरणी में पास करते हैं, तो विशेष रूप से। मैं यह देखने के लिए जांच करने का प्रयास कर रहा हूं कि क्या स्क्रिप्ट को तर्क को एक सरणी के रूप में माना गया था, लेकिन यह काम नहीं करना चाहता है। params = ("$ @") चेक = array_contains $ {params} 'SKIPDIRCHECK' अगर [[$ {check} == 1]]; तब .... लेकिन जब एक तर्क के रूप में स्क्रिप्ट को 'आसस' के साथ चलाया जाता है, तो यह असस कहती रहती है: कमांड नहीं मिला। : /
स्टीव चिल्ड्स

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

ध्यान दें कि यह delimeters और रिक्त स्थान के साथ कोई समस्या नहीं है।


1

वन-लाइन चेक बिना 'grep' और लूप के

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 न ही लूप ।

यहाँ क्या होता है:

  • एक स्ट्रिंग में हमारे आइटम को खोजने के लिए हम वाइल्डकार्ड प्रतिस्थापन मिलानकर्ता का उपयोग करते हैं, जिसे स्ट्रिंग में जोड़ा जाता है;
  • हमने अपने खोज आइटम को सीमांकक की एक जोड़ी के बीच जोड़कर संभव झूठी सकारात्मक को काट दिया;
  • हम गैर-मुद्रण योग्य चरित्र का उपयोग सीमांकक के रूप में करते हैं, सुरक्षित पक्ष पर होने के लिए;
  • हम अपने परिसीमन को अस्थाई प्रतिस्थापन के लिए भी उपयोग कर रहे हैं IFS मान के ;
  • हम IFSएक उप-शेल (कोष्ठकों की एक जोड़ी के अंदर) में अपनी सशर्त अभिव्यक्ति का मूल्यांकन करके इस मूल्य प्रतिस्थापन को अस्थायी बनाते हैं

Dlm को हटा दें। सीधे IFS का उपयोग करें।
रॉबिन ए। मीड

यह सबसे अच्छा जवाब है। मुझे यह बहुत पसंद आया, मैंने इस तकनीक का उपयोग करके एक फ़ंक्शन लिखा
रॉबिन ए। मीड

1

पैरामीटर विस्तार का उपयोग:

$ {पैरामीटर: + शब्द} यदि पैरामीटर अशक्त या परेशान है, तो कुछ भी प्रतिस्थापित नहीं किया जाता है, अन्यथा शब्द का विस्तार प्रतिस्थापित किया जाता है।

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

उत्तर दिए जाने के बाद, मैंने एक और उत्तर पढ़ा जो मुझे विशेष रूप से पसंद था, लेकिन यह त्रुटिपूर्ण और अस्वीकृत था। मैं प्रेरित हुआ और यहां दो नए दृष्टिकोण हैं जो मुझे व्यवहार्य दिखाई देते हैं।

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

का उपयोग कर grepऔर printf:

(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

यहाँ मेरा इस पर ले रहा है।

मैं लूप के लिए एक बैश का उपयोग नहीं करता अगर मैं इसे टाल सकता हूं, क्योंकि इसे चलाने में समय लगता है। यदि कुछ को लूप करना है, तो इसे कुछ ऐसा होने दें जो शेल स्क्रिप्ट की तुलना में निचले स्तर की भाषा में लिखा गया हो।

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

यह एक अस्थायी साहचर्य सरणी बनाकर काम करता है, _arr , जिसके सूचकांक इनपुट सरणी के मान से प्राप्त होते हैं। (ध्यान दें कि साहचर्य सरणियां बैश 4 और इसके बाद के संस्करण में उपलब्ध हैं, इसलिए यह फ़ंक्शन बैश के पुराने संस्करणों में काम नहीं करेगा।) हम $IFSव्हाट्सएप पर शब्द विभाजन से बचने के लिए सेट करते हैं।

फ़ंक्शन में कोई स्पष्ट लूप नहीं है, हालांकि आंतरिक रूप से पॉप्युलेट करने के लिए इनपुट सरणी के माध्यम से कदमों को काटें printf। प्रिंटफ़ प्रारूप का उपयोग %qयह सुनिश्चित करने के लिए किया जाता है कि इनपुट डेटा से बच गए हैं ताकि वे सुरक्षित रूप से सरणी कुंजियों के रूप में उपयोग किए जा सकें।

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

ध्यान दें कि यह फ़ंक्शन जो भी उपयोग करता है वह बिल्ट-इन-बैश है, इसलिए कमांड विस्तार में भी कोई बाहरी पाइप आपको नीचे नहीं खींच रहा है।

और अगर आपको उपयोग करना पसंद नहीं है eval... तो, आप दूसरे दृष्टिकोण का उपयोग करने के लिए स्वतंत्र हैं। :-)


यदि सरणी में वर्ग कोष्ठक हों तो क्या होगा?
गनीउर्फ़_ग्निऑरफ़

@gniourf_gniourf - यदि वर्ग कोष्ठक संतुलित हैं तो ठीक प्रतीत होता है, लेकिन मैं यह देख सकता हूँ कि यदि आपके सरणी में असंतुलित वर्ग कोष्ठक के साथ मान शामिल हैं, तो यह एक समस्या है। उस स्थिति में मैं evalउत्तर के अंत में निर्देश देना चाहूंगा । :)
घटिया

ऐसा नहीं है कि मुझे पसंद नहीं है eval(मेरे पास इसके खिलाफ कुछ भी नहीं है, ज्यादातर लोगों के विपरीत, जो रोना evalबुराई है, ज्यादातर यह समझने के बिना कि इसके बारे में क्या बुराई है)। बस आपकी आज्ञा टूट गई है। शायद %qइसके बजाय %sबेहतर होगा।
गनीउर_ग्निउरफ

1
@gniourf_gniourf: मेरा मतलब केवल "एक और दृष्टिकोण" बिट था (और मैं पूरी तरह से आपके साथ फिर से eval, जाहिर है), लेकिन आप बिल्कुल सही हैं, %qमदद करने के लिए प्रकट होता है, बिना किसी चीज को तोड़ने के जो मैं देख सकता हूं। (मुझे नहीं पता था कि% q वर्ग कोष्ठक से भी बच जाएगा।) मैंने देखा और तय किया गया एक और मुद्दा व्हॉट्सएप के बारे में था। साथ a=(one "two " three), कीगन के मुद्दे के समान: न केवल किया array_contains a "two "एक झूठी नकारात्मक मिलता है, लेकिन array_contains a twoएक झूठी सकारात्मक मिला है। सेटिंग करके ठीक करना आसान है IFS
घटिया

व्हाट्सएप के बारे में, क्या यह नहीं है क्योंकि उद्धरण गायब हैं? यह ग्लोब पात्रों के साथ भी टूटता है। मुझे लगता है कि आप इसके बजाय चाहते हैं: eval _arr=( $(eval printf '[%q]="1"\ ' "\"\${$1[@]}\"") )और आप खाई खोद सकते हैं local IFS=। सरणी में खाली क्षेत्रों के साथ अभी भी एक समस्या है, क्योंकि बैश एक साहचर्य सरणी में एक खाली कुंजी बनाने से इनकार करेगा। इसे ठीक करने के लिए एक त्वरित हैक करने का तरीका एक डमी चरित्र को रोकना है, कहते हैं x: eval _arr=( $(eval printf '[x%q]="1"\ ' "\"\${$1[@]}\"") )और return $(( 1 - 0${_arr[x$2]} ))
gnourf_gniourf

-1

नियमित अभिव्यक्ति तकनीक का मेरा संस्करण जो पहले से ही सुझाया गया है:

values=(foo bar)
requestedValue=bar

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

यहाँ क्या हो रहा है कि आप समर्थित मानों को शब्दों में बदल रहे हैं और एक विशिष्ट स्ट्रिंग, "एक्स-" इस मामले में, उनमें से प्रत्येक के लिए, और अनुरोधित मूल्य के लिए ऐसा ही कर रहे हैं। यदि यह वास्तव में सरणी में समाहित है, तो परिणामी स्ट्रिंग परिणामी टोकन के अधिकांश से मेल खाएगी, या इसके विपरीत बिल्कुल भी नहीं। बाद वाले मामले में || ऑपरेटर ट्रिगर करता है और आप जानते हैं कि आप असमर्थित मूल्य के साथ काम कर रहे हैं। इससे पहले कि सभी अनुरोधित मूल्य मानक शेल स्ट्रिंग हेरफेर के माध्यम से सभी प्रमुख और अनुगामी व्हाट्सएप से छीन लिए जाते हैं।

यह साफ और सुरुचिपूर्ण है, मेरा मानना ​​है, हालांकि मैं यह सुनिश्चित करने के बारे में सुनिश्चित नहीं हूं कि यदि आपके समर्थित मूल्यों की सरणी विशेष रूप से बड़ी है, तो यह कितना अच्छा हो सकता है।


-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

मैं इतने लंबे समय से बैश का उपयोग नहीं कर रहा हूं कि मेरे पास उत्तरों को समझने में कठिन समय है, या यहां तक ​​कि मैंने खुद को क्या लिखा है :) मैं विश्वास नहीं कर सकता कि यह प्रश्न इस समय के बाद भी गतिविधि हो रही है :)
पाओलो टेडेस्को

किस बारे में test_arr=("hello" "world" "two words")?
क्वर्टी

-1

मेरे पास यह मामला था कि मुझे यह जांचना था कि क्या आईडी किसी अन्य स्क्रिप्ट / कमांड द्वारा बनाई गई आईडी की सूची में शामिल है। मेरे लिए निम्नलिखित काम किया:

# 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

मेरे मामले में, मैं आईडी की सूची के लिए कुछ JSON को फ़िल्टर करने के लिए jq चला रहा था और बाद में जाँच करना था कि क्या मेरी आईडी इस सूची में थी और इसने मेरे लिए सबसे अच्छा काम किया। यह प्रकार के मैन्युअल रूप से बनाए गए सरणियों के लिए काम नहीं करेगा, LIST=("1" "2" "4")लेकिन नईलाइन से अलग स्क्रिप्ट आउटपुट के लिए।


पुनश्च: क्योंकि मैं अपेक्षाकृत नया हूँ एक उत्तर टिप्पणी नहीं कर सकता ...


-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 = "तीन" सेट करने से मिलान नहीं होगा।


-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

मैं इस एक के साथ आया था, जो केवल चिड़ियाघर में काम करने के लिए निकलता है, लेकिन मुझे लगता है कि सामान्य दृष्टिकोण अच्छा है।

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

आप प्रत्येक तत्व से अपना पैटर्न तभी निकालते हैं, जब यह उसके साथ शुरू ${arr[@]/#pattern/}या समाप्त होता ${arr[@]/%pattern/}है। ये दो प्रतिस्थापन बाश में काम करते हैं, लेकिन एक ही समय में दोनों${arr[@]/#%pattern/} केवल zsh में काम करते हैं।

यदि संशोधित सरणी मूल के बराबर है, तो इसमें तत्व नहीं है।

संपादित करें:

यह एक काम करता है:

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

प्रतिस्थापन के बाद यह दोनों सरणियों की लंबाई की तुलना करता है। Obly अगर सरणी में तत्व शामिल है तो प्रतिस्थापन इसे पूरी तरह से हटा देगा, और गिनती अलग होगी।

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.