Differences between revisions 2 and 17 (spanning 15 versions)
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.
Revision 17 as of 2023-04-29 04:22:40
Size: 2004
Editor: ormaaj
Comment: Changes. Ignoring 5.2 for now
Deletions are marked like this. Additions are marked like this.
Line 1: Line 1:
[[Anchor(faq46)]] <<Anchor(faq46)>>
Line 3: Line 3:
If your real question is ''How do I check whether one of my parameters was -v?'' then see [[BashFAQ/035|FAQ #35]] instead. Otherwise, read on…
Line 4: Line 5:
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'''. <<TableOfContents>>
=== Associative arrays ===
All we need to do is create an entry for each item and look it up by index. In this example, we test whether the user input `x` is a member of the set `a`:
Line 6: Line 9:
'''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. {{{
# Bash etc.
Line 8: Line 12:
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
function get_input {
 [[ -t 0 ]] || return
 printf 'hm? '
 IFS= read -r${BASH_VERSION+\e} "$1"
Line 28: Line 18:
if isIn "jacob" "${names[@]}"
then
    echo "Jacob is on the list."
set -- Bigfoot UFOs Republicans
typeset -A a
for x; do
 a+=([$x]=)
done

get_input x

if [[ -v a[$x] ]]; then
 printf '%s exists!\n' "$x"
else
 printf '%s doesn't exist.\n' "$x"
Line 34: Line 33:
Or, if you want your function to return the '''index''' at which the element was found: === Indexed arrays ===
We can store a list of strings in an indexed array by looping over each element:
Line 36: Line 37:
indexOf() {
    pattern=$1
    shift
# Bash
Line 40: Line 39:
    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
typeset -a haystack
for x in "${haystack[@]}"; do
 [[ $x == "$needle" ]] && printf 'Found %q!\n' "$needle"
done
Line 59: Line 45:
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}}}
=== enum (ksh93) ===
In ksh93t or later, one may create enum types/variables/constants using the `enum` builtin. These work similarly to C enums (and the equivalent feature of other languages). These may be used to restrict which values may be assigned to a variable so as to avoid the need for an expensive test each time an array variable is set or referenced. Like types created using `typeset -T`, the result of an `enum` command is a new declaration command that can be used to instantiate objects of that type.
Line 65: Line 48:
Here's a shorter way of doing it:
   {{{
   if [[ " $foo " = *" $bar "* ]]; then
      echo "Found $bar."
   fi}}}
{{{
# ksh93
Line 71: Line 51:
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}}}
 $ enum colors=(red green blue)
 $ colors foo=green
 $ foo=yellow
ksh: foo: invalid value yellow
}}}
Line 77: Line 57:
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: `typeset -a` can also be used in combination with an enum type to allow enum constants as subscripts.
Line 79: Line 59:
   {{{
   # 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 "-".
{{{
# ksh93
Line 87: Line 62:
   # 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.
   
 $ typeset -a '[colors]' bar
 $ bar[blue]=test1
 $ typeset -p bar
typeset -a '[colors]' bar=([blue]=test)
 $ bar[orange]=test
ksh: colors: invalid value orange
}}}

----
CategoryShell

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

If your real question is How do I check whether one of my parameters was -v? then see FAQ #35 instead. Otherwise, read on…

Associative arrays

All we need to do is create an entry for each item and look it up by index. In this example, we test whether the user input x is a member of the set a:

# Bash etc.

function get_input {
        [[ -t 0 ]] || return
        printf 'hm? '
        IFS= read -r${BASH_VERSION+\e} "$1"
}

set -- Bigfoot UFOs Republicans
typeset -A a
for x; do
        a+=([$x]=)
done

get_input x

if [[ -v a[$x] ]]; then
        printf '%s exists!\n' "$x"
else
        printf '%s doesn't exist.\n' "$x"
fi

Indexed arrays

We can store a list of strings in an indexed array by looping over each element:

# Bash

typeset -a haystack
for x in "${haystack[@]}"; do
        [[ $x == "$needle" ]] && printf 'Found %q!\n' "$needle"
done

enum (ksh93)

In ksh93t or later, one may create enum types/variables/constants using the enum builtin. These work similarly to C enums (and the equivalent feature of other languages). These may be used to restrict which values may be assigned to a variable so as to avoid the need for an expensive test each time an array variable is set or referenced. Like types created using typeset -T, the result of an enum command is a new declaration command that can be used to instantiate objects of that type.

# ksh93

 $ enum colors=(red green blue)
 $ colors foo=green
 $ foo=yellow
ksh: foo:  invalid value yellow

typeset -a can also be used in combination with an enum type to allow enum constants as subscripts.

# ksh93

 $ typeset -a '[colors]' bar
 $ bar[blue]=test1
 $ typeset -p bar
typeset -a '[colors]' bar=([blue]=test)
 $ bar[orange]=test
ksh: colors:  invalid value orange


CategoryShell

BashFAQ/046 (last edited 2023-04-29 04:33:04 by ormaaj)