Проверьте, содержит ли массив Bash значение - PullRequest
353 голосов
/ 10 сентября 2010

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

Редактировать : С помощью ответов и комментариев после некоторого тестирования я придумал следующее:

function contains() {
    local n=$#
    local value=${!n}
    for ((i=1;i < $#;i++)) {
        if [ "${!i}" == "${value}" ]; then
            echo "y"
            return 0
        fi
    }
    echo "n"
    return 1
}

A=("one" "two" "three four")
if [ $(contains "${A[@]}" "one") == "y" ]; then
    echo "contains one"
fi
if [ $(contains "${A[@]}" "three") == "y" ]; then
    echo "contains three"
fi

Не уверен,лучшее решение, но, похоже, работает.

Ответы [ 32 ]

9 голосов
/ 03 мая 2012
containsElement () { for e in "${@:2}"; do [[ "$e" = "$1" ]] && return 0; done; return 1; }

Теперь правильно обрабатывает пустые массивы.

6 голосов
/ 10 сентября 2010

Если вы хотите выполнить быстрый и грязный тест, чтобы увидеть, стоит ли перебирать весь массив для получения точного соответствия, Bash может рассматривать массивы как скаляры.Проверьте на совпадение в скаляре, если его нет, тогда пропуск цикла экономит время.Очевидно, вы можете получить ложные срабатывания.

array=(word "two words" words)
if [[ ${array[@]} =~ words ]]
then
    echo "Checking"
    for element in "${array[@]}"
    do
        if [[ $element == "words" ]]
        then
            echo "Match"
        fi
    done
fi

Это выведет «Проверка» и «Совпадение».При array=(word "two words" something) будет выводиться только «Проверка».С array=(word "two widgets" something) вывод не будет.

5 голосов
/ 17 июля 2015
a=(b c d)

if printf '%s\0' "${a[@]}" | grep -Fqxz c
then
  echo 'array “a” contains value “c”'
fi

Если вы предпочитаете, вы можете использовать эквивалентные длинные опции:

--fixed-strings --quiet --line-regexp --null-data
4 голосов
/ 18 ноября 2016

Это работает для меня:

# traditional system call return values-- used in an `if`, this will be true when returning 0. Very Odd.
contains () {
    # odd syntax here for passing array parameters: http://stackoverflow.com/questions/8082947/how-to-pass-an-array-to-a-bash-function
    local list=$1[@]
    local elem=$2

    # echo "list" ${!list}
    # echo "elem" $elem

    for i in "${!list}"
    do
        # echo "Checking to see if" "$i" "is the same as" "${elem}"
        if [ "$i" == "${elem}" ] ; then
            # echo "$i" "was the same as" "${elem}"
            return 0
        fi
    done

    # echo "Could not find element"
    return 1
}

Пример вызова:

arr=("abc" "xyz" "123")
if contains arr "abcx"; then
    echo "Yes"
else
    echo "No"
fi
3 голосов
/ 12 сентября 2013

дано:

array=("something to search for" "a string" "test2000")
elem="a string"

, затем простая проверка:

if c=$'\x1E' && p="${c}${elem} ${c}" && [[ ! "${array[@]/#/${c}} ${c}" =~ $p ]]; then
  echo "$elem exists in array"
fi

, где

c is element separator
p is regex pattern

(причина назначения p отдельно, а не использованиявыражение прямо внутри [[]] должно поддерживать совместимость для bash 4)

3 голосов
/ 13 сентября 2017

Заимствуя у Денниса Уильямсона * answer , следующее решение объединяет массивы, безопасное для оболочки цитирование и регулярные выражения, чтобы избежать необходимости: повторять циклы;использование труб или других подпроцессов;или с использованием утилит не-bash.

declare -a array=('hello, stack' one 'two words' words last)
printf -v array_str -- ',,%q' "${array[@]}"

if [[ "${array_str},," =~ ,,words,, ]]
then
   echo 'Matches'
else
   echo "Doesn't match"
fi

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

  1. Создайте строку сравнения, используя встроенную в Bash printf shell-цитирование %q.Заключение в кавычки гарантирует, что специальные символы станут «безопасными для оболочки», если экранировать их с помощью обратной косой черты \.
  2. Выберите специальный символ, который будет служить разделителем значений.Разделитель ДОЛЖЕН быть одним из специальных символов, которые будут экранированы при использовании %q;это единственный способ гарантировать, что значения в массиве не могут быть построены умными способами, чтобы обмануть совпадение регулярного выражения.Я выбираю запятую ,, потому что этот символ наиболее безопасен, когда eval'd или неправильно используется иным неожиданным образом.
  3. Объедините все элементы массива в одну строку, используя два экземпляраспециальный символ, чтобы служить разделителем.Используя запятую в качестве примера, я использовал ,,%q в качестве аргумента для printf.Это важно, потому что два экземпляра специального символа могут появляться рядом друг с другом, только когда они появляются в качестве разделителя;все другие экземпляры специального символа будут экранированы.
  4. Добавьте два завершающих экземпляра разделителя в строку, чтобы разрешить совпадения с последним элементом массива.Таким образом, вместо сравнения с ${array_str}, сравните с ${array_str},,.
  5. Если искомая целевая строка предоставляется пользовательской переменной, вы должны экранировать все экземплярыспециальный символ с обратной косой чертой.В противном случае совпадение регулярного выражения становится уязвимым для того, чтобы его обманули искусно созданные элементы массива.
  6. Выполните совпадение регулярного выражения Bash со строкой.
2 голосов
/ 01 ноября 2016

Использование grep и printf

Отформатируйте каждый элемент массива в новой строке, затем grep в строках.

if printf '%s\n' "${array[@]}" | grep -x -q "search string"; then echo true; else echo false; fi
пример:
$ array=("word", "two words")
$ if printf '%s\n' "${array[@]}" | grep -x -q "two words"; then echo true; else echo false; fi
true

Обратите внимание, что здесь нет проблем с разделителями и пробелами.

2 голосов
/ 09 сентября 2017

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

$find="myword"
$array=(value1 value2 myword)
if [[ ! -z $(printf '%s\n' "${array[@]}" | grep -w $find) ]]; then
  echo "Array contains myword";
fi

Это не сработает при word или val, только совпадения всего слова. Он сломается, если каждое значение массива будет содержать несколько слов.

2 голосов
/ 29 сентября 2013

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

Вот версия, которая работает с именем массива:

function array_contains # array value
{
    [[ -n "$1" && -n "$2" ]] || {
        echo "usage: array_contains <array> <value>"
        echo "Returns 0 if array contains value, 1 otherwise"
        return 2
    }

    eval 'local values=("${'$1'[@]}")'

    local element
    for element in "${values[@]}"; do
        [[ "$element" == "$2" ]] && return 0
    done
    return 1
}

При этом пример вопроса становится:

array_contains A "one" && echo "contains one"

и т.д.

1 голос
/ 20 сентября 2018

Небольшое дополнение к ответу @ ghostdog74 об использовании логики case для проверки того, что массив содержит определенное значение:

myarray=(one two three)
word=two
case "${myarray[@]}" in  ("$word "*|*" $word "*|*" $word") echo "found" ;; esac

Или с включенной опцией extglob, вы можете сделать это так:

myarray=(one two three)
word=two
shopt -s extglob
case "${myarray[@]}" in ?(*" ")"$word"?(" "*)) echo "found" ;; esac

Также мы можем сделать это с if оператором:

myarray=(one two three)
word=two
if [[ $(printf "_[%s]_" "${myarray[@]}") =~ .*_\[$word\]_.* ]]; then echo "found"; fi
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...