Как проверить, существует ли переменная в списке в BASH

Я пытаюсь написать script в bash, который проверяет правильность ввода пользователя.
Я хочу совместить вход (например, переменную x) со списком допустимых значений.

то, что я придумал в данный момент:

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

Мой вопрос в том, есть ли более простой способ сделать это,
что-то вроде list.contains(x) для большинства языков программирования.

Дополнение:
Список слов:

list="11 22 33"

мой код будет эхо-сообщение только для этих значений, так как list рассматривается как массив, а не строка, все манипуляции с строкой будут проверять 1, пока я хочу, чтобы он потерпел неудачу.

Ответ 1

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

или создать функцию:

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

чтобы использовать его:

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

Ответ 2

Матвей прав, но вы должны цитировать $x и рассматривать любые "пробелы" (например, новую строку) с помощью

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

так, то есть

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

end then

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

или

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

Обратите внимание, что вы должны использовать "" в случае переменных:

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

Ответ 3

Вы можете использовать (* подстановочные знаки) за пределами оператора case, если вы используете двойные скобки:

string='My string';

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

Ответ 4

Самое простое решение IMHO состоит в том, чтобы добавить и добавить исходную строку с пробелом и проверить регулярное выражение с помощью [[ ]]

haystack='foo bar'
needle='bar'

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

это не будет ложным положительным для значений со значениями, содержащими иглу в качестве подстроки, например. с haystack foo barbaz.

(Концепция бесстыдно украдена в форме JQuery hasClass() -Method)

Ответ 5

как насчет

echo $list|grep $x

вы можете проверить выход или $? указанной выше строки, чтобы принять решение.

Ответ 6

Мне легче использовать форму echo $LIST | xargs -n1 echo | grep $VALUE, как показано ниже:

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

Это работает для списка, разделенного пробелами, но вы можете адаптировать его к любому другому разделителю (например, :), выполнив следующие действия:

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

Обратите внимание, что для тестирования необходимо < <24 > .

Ответ 7

Рассмотрим использование ключей ассоциативных массивов. Я бы предположил, что это превосходит как соответствие регулярному выражению/шаблону, так и цикл, хотя я не профилировал его.

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

Вывод:

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 )

Ответ 8

Если список зафиксирован в script, мне нравится следующее:

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

Затем используйте validate "$x", чтобы проверить, разрешено ли $x.

Если вы хотите использовать однострочный шрифт и не интересуетесь пробелами в именах элементов, вы можете использовать это (обратите внимание -w вместо -x):

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

Примечания:

  • Это соответствует POSIX sh.
  • validate не принимает подстроки (удалите параметр -x для grep, если вы этого хотите).
  • validate интерпретирует свой аргумент как фиксированную строку, а не регулярную выражение (удалите параметр -F для grep, если вы этого хотите).

Пример кода для выполнения функции:

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

Ответ 9

Если ваш список значений должен быть жестко закодирован в script, его довольно просто проверить с помощью case. Вот краткий пример, который вы можете адаптировать к своим требованиям:

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

Если список является переменной массива во время выполнения, один из других ответов, вероятно, лучше подходит.

Ответ 10

Примеры

$ 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

in_list

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 "[email protected]"
do
  if [ "$SEARCH_FOR" == "$ITEM" ]
  then
    echo "YES"
    exit;
  fi
done

echo "NO"

Ответ 11

Предполагая, что переменная TARGET может быть только "биномиальной" или "регрессией", тогда следующее:

# 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

Вы можете добавить больше строк в список, разделив их на | (трубе).

Преимущество использования egrep заключается в том, что вы можете легко добавить нечувствительность к регистру (-i) или проверить более сложные сценарии с регулярным выражением.

Ответ 12

Думал, что добавлю свое решение в список.

# 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

Ответ 13

Это почти ваше первоначальное предложение, но почти 1-лайнер. Не так сложно, как другие действительные ответы, и не так зависит от версий bash (может работать со старыми bashes).

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

Думаю, мое предложение все еще может быть улучшено как по длине, так и по стилю.