Differences between revisions 1 and 2
Revision 1 as of 2007-05-02 23:37:18
Size: 2011
Editor: redondos
Comment:
Revision 2 as of 2008-05-08 07:54:33
Size: 3277
Editor: Lhunath
Comment: add a note about lists in strings, add a function to search for elements.
Deletions are marked like this. Additions are marked like this.
Line 4: Line 4:
The safest way to do this would be to loop over all elements in your set/list and check them for the element/word you are looking for. Say we are looking for the content of bar in the array foo: First of all, let's get the terminoligy straight. Bash has no notion of 'lists' or 'sets' or any such. Bash has strings and arrays. Strings are a 'list' of '''characters''', arrays are a 'list' of '''strings'''.

'''NOTE:''' A string can not possibly contain a list of other strings because there is no way to reliably tell where each of those other strings end and the next other string starts.

The only proper way to do this is to loop over all elements in your array and check them for the element you are looking for. Say what we are looking for is in `bar` and our list is in the array `foo`:
Line 10: Line 14:
Or, to stop searching when you find it:
   {{{
   for element in "${foo[@]}"; do
      [[ $element = $bar ]] && { echo "Found $bar."; break; }
   done}}}
If you need to perform this several times in your script, you might want to extract the logic into a function:
{{{
isIn() {
    pattern=$1
    shift
Line 16: Line 20:
If for some reason your list/set is not in an array, but is a string of words, and the element you are searching for is also a word, you can use this:     for element
    do
        [[ $element = $pattern ]] && return 0
    done

    return 1
}

if isIn "jacob" "${names[@]}"
then
    echo "Jacob is on the list."
fi
}}}

Or, if you want your function to return the '''index''' at which the element was found:
{{{
indexOf() {
    pattern=$1
    shift

    list=("$@")
    for index in "${!list[@]}"
    do
        [[ ${list[index]} = $pattern ]] && {
            echo $index
            return
        }
    done

    echo -1
    return 1
}

if index=$(indexOf "jacob" "${names[@]}")
then
    echo "Jacob is the ${index}th on the list."
fi
}}}

If your 'list' is contained in a string, and for some half-witted reason you choose not to heed warning to the note above, you can use the following code to search through 'words' in a string; where a word is defined by any substring that is delimited by whitespace (or more specifically, the characters currently in IFS):
Line 22: Line 65:
A less safe, but more clever version: Here's a shorter way of doing it:
Line 24: Line 67:
   if [[ " $foo " = *\ "$bar"\ * ]]; then    if [[ " $foo " = *" $bar "* ]]; then
Line 34: Line 77:
GNU's grep has a {{{\b}}} feature which allegedly matches the edges of words. Using that, one may attempt to replicate the "clever" approach used above, but it is fraught with peril: GNU's grep has a {{{\b}}} feature which allegedly matches the edges of words. Using that, one may attempt to replicate the shorter approach used above, but it is fraught with peril:
Line 48: Line 91:
Since this "feature" of GNU grep is both non-portable and poorly defined, we don't recommend using it. Since this "feature" of GNU grep is both non-portable and poorly defined, we recommend '''not''' using it.  It is simply mentioned here for the sake of completion.

Anchor(faq46)

I want to check to see whether a word is in a list (or an element is a member of a set).

First of all, let's get the terminoligy straight. Bash has no notion of 'lists' or 'sets' or any such. Bash has strings and arrays. Strings are a 'list' of characters, arrays are a 'list' of strings.

NOTE: A string can not possibly contain a list of other strings because there is no way to reliably tell where each of those other strings end and the next other string starts.

The only proper way to do this is to loop over all elements in your array and check them for the element you are looking for. Say what we are looking for is in bar and our list is in the array foo:

  •    for element in "${foo[@]}"; do
          [[ $element = $bar ]] && echo "Found $bar."
       done

If you need to perform this several times in your script, you might want to extract the logic into a function:

isIn() {
    pattern=$1
    shift

    for element
    do
        [[ $element = $pattern ]] && return 0
    done

    return 1
}

if isIn "jacob" "${names[@]}"
then 
    echo "Jacob is on the list."
fi

Or, if you want your function to return the index at which the element was found:

indexOf() {
    pattern=$1
    shift

    list=("$@")
    for index in "${!list[@]}"
    do
        [[ ${list[index]} = $pattern ]] && {
            echo $index
            return
        }
    done

    echo -1
    return 1
}

if index=$(indexOf "jacob" "${names[@]}")
then
    echo "Jacob is the ${index}th on the list."
fi

If your 'list' is contained in a string, and for some half-witted reason you choose not to heed warning to the note above, you can use the following code to search through 'words' in a string; where a word is defined by any substring that is delimited by whitespace (or more specifically, the characters currently in IFS):

  •    for element in $foo; do
          [[ $element = $bar ]] && echo "Found $bar."
       done

Here's a shorter way of doing it:

  •    if [[ " $foo " = *" $bar "* ]]; then
          echo "Found $bar."
       fi

And, if for some reason you don't know the syntax of for well enough, here's how to check your script's parameters for an element. For example, '-v':

  •    for element; do
          [[ $element = '-v' ]] && echo "Switching to verbose mode."
       done

GNU's grep has a \b feature which allegedly matches the edges of words. Using that, one may attempt to replicate the shorter approach used above, but it is fraught with peril:

  •    # Is 'foo' one of the positional parameters? 
       egrep '\bfoo\b' <<<"$@" >/dev/null && echo yes
       # This is where it fails: is '-v' one of the positional parameters?
       egrep '\b-v\b' <<<"$@" >/dev/null && echo yes
       # Unfortunately, \b sees "v" as a separate word.
       # Nobody knows what the hell it's doing with the "-".
    
       # Is "someword" in the array 'array'?
       egrep '\bsomeword\b' <<<"${array[@]}"
       # Obviously, you can't use this if someword is '-v'!

Since this "feature" of GNU grep is both non-portable and poorly defined, we recommend not using it. It is simply mentioned here for the sake of completion.

BashFAQ/046 (last edited 2019-05-20 20:45:14 by GreyCat)