Как проверить, существует ли переменная в списке в BASH - PullRequest
113 голосов
/ 09 ноября 2011

Я пытаюсь написать скрипт на 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, в то время как я хотел бы, чтобы он потерпел неудачу.

Ответы [ 13 ]

134 голосов
/ 09 ноября 2011
[[ $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
30 голосов
/ 09 декабря 2013

Матвей прав, но вы должны заключить в кавычки $ 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 затем

`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"
13 голосов
/ 09 ноября 2011

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

string='My string';

if [[ $string == *My* ]]
then
echo "It's there!";
fi
11 голосов
/ 04 октября 2017

ИМХО самое простое решение - добавить и добавить исходную строку с пробелом и проверить регулярное выражение с [[ ]]

haystack='foo bar'
needle='bar'

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

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

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

10 голосов
/ 09 ноября 2011

как насчет

echo $list|grep $x

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

6 голосов
/ 30 июня 2014

Если список исправлен в скрипте, мне больше всего нравится следующее:

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
5 голосов
/ 01 февраля 2017

Я считаю, что проще использовать форму 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

Обратите внимание, что для работы теста требуется ".

5 голосов
/ 08 ноября 2016

Если ваш список значений должен быть жестко запрограммирован в скрипте, его довольно просто проверить с помощью 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

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

4 голосов
/ 10 февраля 2014

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

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 )
1 голос
/ 07 декабря 2018

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

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

Полагаю, мое предложение еще можно улучшить как по длине, так и по стилю.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...