3757
Comment: clean up
|
2007
--
|
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: |
If your real question was ''How do I check whether one of my parameters was -v?'' then please see [:BashFAQ/035:FAQ #35] instead. | <<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: |
First of all, let's get the terminology straight. Bash has no notion of "lists" or "sets" or any such. Bash has strings and [:BashFAQ/005:arrays]. Strings are a "list" of '''characters''', arrays are a "list" of '''strings'''. | {{{ # Bash etc. |
Line 8: | Line 12: |
'''NOTE:''' In the general case, a string cannot possibly contain a list of other strings because there is no reliable way to tell where each substring begins and ends. | function get_input { [[ -t 0 ]] || return printf 'hm? ' IFS= read -r${BASH_VERSION+\e} -- "$1" } |
Line 10: | Line 18: |
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`: {{{ # Bash for element in "${foo[@]}"; do [[ $element = $bar ]] && echo "Found $bar." done}}} |
set -- Bigfoot UFOs Republicans typeset -A a for x; do a+=([$x]=) done |
Line 17: | Line 24: |
If you need to perform this several times in your script, you might want to extract the logic into a function: {{{ # Bash isIn() { local pattern="$1" element shift for element do [[ $element = $pattern ]] && return 0 done return 1 } if isIn "jacob" "${names[@]}" then echo "Jacob is on the list." fi }}} |
get_input x |
Line 38: | Line 26: |
Or, if you want your function to return the '''index''' at which the element was found: {{{ # Bash 3.0 or higher indexOf() { local pattern=$1 local index list shift list=("$@") for index in "${!list[@]}" do [[ ${list[index]} = $pattern ]] && { echo $index return 0 } done echo -1 return 1 } if index=$(indexOf "jacob" "${names[@]}") then echo "Jacob is the ${index}th on the list." else echo "Jacob is not on the list." fi }}} |
if [[ -v a[$x] ]]; then printf '%s exists!\n' "$x" else printf '%s doesn't exist.\n' "$x" fi }}} |
Line 67: | Line 33: |
If your "list" is contained in a string, and for some half-witted reason you choose not to heed the warnings above, you can use the following code to search through "words" in a string. (The only real excuse for this would be that you're stuck in Bourne shell, which has no arrays.) {{{ # Bourne for element in $foo; do if test x"$element" = x"$bar"; then echo "Found $bar." fi done}}} Here, a "word" is defined as any substring that is delimited by whitespace (or more specifically, the characters currently in IFS). |
=== Indexed arrays === We can store a list of strings in an indexed array by looping over each element: |
Line 77: | Line 36: |
Here's a hack that you shouldn't use, but which is presented for the sake of completeness: {{{ # Bash if [[ " $foo " = *" $bar "* ]]; then echo "Found $bar." fi}}} |
{{{ # Bash |
Line 84: | Line 39: |
That same hack, for Bourne shells: {{{ # Bourne case " $foo " in *" $bar "*) echo "Found $bar.";; esac}}} |
typeset -a haystack for x in "${haystack[@]}"; do [[ $x == "$needle" ]] && printf 'Found %q!\n' "$needle" done }}} |
Line 91: | Line 45: |
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: | === 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 93: | Line 48: |
{{{ # Is 'foo' one of the positional parameters? egrep '\bfoo\b' <<<"$@" >/dev/null && echo yes |
{{{ # ksh93 |
Line 97: | Line 51: |
# 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 "-". |
$ enum colors=(red green blue) $ colors foo=green $ foo=yellow ksh: foo: invalid value yellow }}} |
Line 102: | Line 57: |
# 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 completeness. |
`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 |
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…
Contents
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