Преимущество этого подхода заключается в том, что нет необходимости перебирать все элементы (по крайней мере, явно). Но поскольку array_to_string_internal()
в файле array.c все еще выполняется циклическая обработка элементов массива и объединение их в строку, он, вероятно, не более эффективен, чем предложенные циклические решения, но он более читабелен.
if [[ " ${array[@]} " =~ " ${value} " ]]; then
# whatever you want to do when array contains value
fi
if [[ ! " ${array[@]} " =~ " ${value} " ]]; then
# whatever you want to do when array doesn't contain value
fi
Обратите внимание, что в тех случаях, когда искомым значением является одно из слов в элементе массива с пробелами, оно даст ложные срабатывания. Например
array=("Jack Brown")
value="Jack"
Регулярное выражение будет видеть «Джек» как находящийся в массиве, хотя это не так. Поэтому вам придется изменить IFS
и символы-разделители в вашем регулярном выражении, если вы все еще хотите использовать это решение, как это
IFS=$'\t'
array=("Jack Brown\tJack Smith")
unset IFS
value="Jack"
if [[ "\t${array[@]}\t" =~ "\t${value}\t" ]]; then
echo "true"
else
echo "false"
fi
Это напечатает «ложь».
Очевидно, что это также может быть использовано в качестве выражения теста, что позволяет выразить его в виде одной строки
[[ " ${array[@]} " =~ " ${value} " ]] && echo "true" || echo "false"
[[ " ${branches[@]} " =~ " ${value} " ]] && echo "YES" || echo "NO";
if
. Я не думаю, что есть способ избежать SC2199 с таким подходом. Вам придется явно зацикливать массив, как показано в некоторых других решениях, или игнорировать предупреждение.Ниже приведена небольшая функция для достижения этой цели. Строка поиска является первым аргументом, а остальные - элементами массива:
Тестовый запуск этой функции может выглядеть так:
источник
"${array[@]}"
. В противном случае элементы, содержащие пробелы, нарушат функциональность.if elementIn "$table" "${skip_tables[@]}" ; then echo skipping table: ${table}; fi;
Спасибо за вашу помощь!shift
сдвигает список аргументов на 1 влево (отбрасывая первый аргумент) иfor
безin
неявно перебирает список аргументов.источник
case "${myarray[@]}" in *"t"*) echo "found" ;; esac
выводы:found
Для строк:
источник
${#}
поскольку Bash поддерживает разреженные массивы.Однолинейное решение
объяснение
printf
Оператор печатает каждый элемент массива на отдельной строке.grep
Оператор использует специальные символы^
и$
не найти строку , которая содержит точно по образцу , данному вmypattern
(не больше, не меньше).Применение
Чтобы поместить это в
if ... then
утверждение:Я добавил
-q
флаг кgrep
выражению, чтобы оно не печатало совпадения; это просто будет относиться к существованию совпадения как к «правде».источник
Если вам нужна производительность, вы не хотите перебирать весь массив каждый раз при поиске.
В этом случае вы можете создать ассоциативный массив (хеш-таблицу или словарь), который представляет индекс этого массива. Т.е. он отображает каждый элемент массива в свой индекс в массиве:
Тогда вы можете использовать это так:
И проверить членство так:
Или также:
Обратите внимание, что это решение работает правильно, даже если в проверенном значении или в значениях массива есть пробелы.
В качестве бонуса вы также получаете индекс значения в массиве с помощью:
источник
make_index
немного более изобретательный из-за косвенности; Вы могли бы использовать фиксированное имя массива с гораздо более простым кодом.Я обычно просто использую:
ненулевое значение указывает, что совпадение было найдено.
источник
haystack=(needle1 needle2); echo ${haystack[@]} | grep -o "needle" | wc -w
inarray=$(printf ",%s" "${haystack[@]}") | grep -o ",needle" | wc -w)
inarray=$(printf ",%s" "${haystack[@]}") | grep -x "needle" | wc -l
inarray=$(echo " ${haystack[@]}" | grep -o " needle" | wc -w)
-x заставляет grep пытаться сопоставить всю входную строкуЕще один лайнер без функции:
Спасибо @Qwerty за советы по поводу пробелов!
соответствующая функция:
пример:
источник
exit 0
делает (останавливается как можно скорее, если найден).|| echo not found
Вместо одного конца должен стоять конец строки,|| not found
иначе оболочка попытается выполнить команду по имени not с найденным аргументом, если запрошенное значение отсутствует в массиве.Теперь обрабатывает пустые массивы правильно.
источник
"$e" = "$1"
(вместо"$e" == "$1"
), который выглядит как ошибка."e" == "$1"
синтаксически понятнее.Вот небольшой вклад:
Примечание: этот способ не различает регистр «два слова», но в вопросе этого не требуется.
источник
Если вы хотите выполнить быстрый и грязный тест, чтобы увидеть, стоит ли выполнять итерацию по всему массиву, чтобы получить точное совпадение, Bash может рассматривать массивы как скаляры. Проверьте на совпадение в скаляре, если его нет, тогда пропуск цикла экономит время. Очевидно, вы можете получить ложные срабатывания.
Это выведет «Проверка» и «Совпадение». При
array=(word "two words" something)
этом будет выводиться только «Проверка». Сarray=(word "two widgets" something)
не будет никакого выхода.источник
words
регулярное выражение,^words$
которое соответствует только всей строке, что полностью исключает необходимость проверки каждого элемента в отдельности?pattern='^words$'; if [[ ${array[@]} =~ $pattern ]]
что никогда не будет совпадать, так как он проверяет весь массив сразу, как если бы он был скалярным. Индивидуальные проверки в моем ответе должны проводиться только в том случае, если есть причина продолжить на основе приблизительного соответствия.Это работает для меня:
Пример вызова:
источник
Если вы предпочитаете, вы можете использовать эквивалентные длинные опции:
источник
Заимствуя Dennis Williamson «s ответ , следующие решение объединяет в себе массивы, оболочки-безопасное цитирование, и регулярные выражения , чтобы избежать необходимости: итерация петель; использование труб или других подпроцессов; или используя утилиты не Bash.
Приведенный выше код работает с использованием регулярных выражений Bash для сопоставления со строкой версии содержимого массива. Есть шесть важных шагов, чтобы гарантировать, что совпадение с регулярным выражением не может быть одурачено умными комбинациями значений в массиве:
printf
оболочке-цитировали,%q
. Заключение в кавычки гарантирует, что специальные символы станут «безопасными для оболочки», будучи экранированными с помощью обратной косой черты\
.%q
; это единственный способ гарантировать, что значения в массиве не могут быть построены умными способами, чтобы обмануть совпадение регулярного выражения. Я выбираю запятую,
потому что этот персонаж самый безопасный, когда eval'd или неправильно используется иначе неожиданным образом.,,%q
в качестве аргументаprintf
. Это важно, потому что два экземпляра специального символа могут появляться рядом друг с другом, только когда они появляются в качестве разделителя; все остальные экземпляры специального символа будут экранированы.${array_str}
, сравнивать с${array_str},,
.источник
printf -v pattern ',,%q,,' "$user_input"; if [[ "${array_str},," =~ $pattern ]]
возможно.case "$(printf ,,%q "${haystack[@]}"),," in (*"$(printf ,,%q,, "$needle")"*) true;; (*) false;; esac
Небольшое дополнение к ответу @ ghostdog74 об использовании
case
логики для проверки того, что массив содержит определенное значение:Или с
extglob
включенной опцией, вы можете сделать это так:Также мы можем сделать это с
if
заявлением:источник
данный :
тогда простая проверка:
где
(Причиной назначения p отдельно, а не использования выражения непосредственно внутри [[]] является сохранение совместимости для bash 4)
источник
Комбинируя некоторые из представленных здесь идей, вы можете создать элегантную статистику без петель, которая точно соответствует слову .
Это не сработает
word
илиval
только совпадения всего слова. Он сломается, если каждое значение массива будет содержать несколько слов.источник
Я обычно пишу такие утилиты для работы с именем переменной, а не со значением переменной, главным образом потому, что bash не может иначе передать переменные по ссылке.
Вот версия, которая работает с именем массива:
С этим, пример вопроса становится:
и т.п.
источник
Использование
grep
иprintf
Отформатируйте каждый элемент массива в новой строке, затем
пример:grep
в строках.Обратите внимание, что это не имеет проблем с разделителями и пробелами.
источник
Однострочная проверка без 'grep' и циклов
Этот подход не использует ни внешние утилиты, такие как
grep
циклы.Что здесь происходит, это:
IFS
значения переменной;IFS
замену значения временной, вычисляя наше условное выражение в под-оболочке (внутри пары скобок)источник
Используя расширение параметра:
источник
${myarray[hello]:+_}
отлично работает для ассоциативных массивов, но не для обычных индексированных массивов. Вопрос в том, чтобы найти значение в массиве, а не проверять, существует ли ключ ассоциативного массива.После ответа я прочитал еще один ответ, который мне особенно понравился, но он был ошибочным и опровергнутым. Я вдохновился, и вот два новых подхода, которые я вижу жизнеспособными.
используя
grep
иprintf
:используя
for
:Для not_found результатов добавить
|| <run_your_if_notfound_command_here>
источник
Вот мой взгляд на это.
Я бы предпочел не использовать цикл для bash, если я могу избежать этого, так как для этого требуется время. Если что-то должно зацикливаться, пусть это будет что-то написанное на языке более низкого уровня, чем сценарий оболочки.
Это работает путем создания временного ассоциативного массива,
_arr
индексы которого получены из значений входного массива. (Обратите внимание, что ассоциативные массивы доступны в bash 4 и выше, поэтому эта функция не будет работать в более ранних версиях bash.) Мы установили$IFS
чтобы избежать разбиения слов в пробелах.Функция не содержит явных циклов, хотя внутренняя часть bash проходит через входной массив для заполнения
printf
. Формат printf используется%q
для гарантии того, что входные данные экранированы, чтобы их можно было безопасно использовать в качестве ключей массива.Обратите внимание, что все, что использует эта функция, является встроенным в bash, поэтому нет никаких внешних каналов, тянущих вас вниз, даже в расширении команды.
И если вам не нравится
eval
... ну, вы можете использовать другой подход. :-)источник
eval
инструкцию в конце ответа. :)eval
(я ничего не имею против этого, в отличие от большинства людей, которые плачут,eval
это зло, в основном не понимая, что в этом плохого). Просто ваша команда нарушена. Может быть,%q
вместо этого%s
будет лучше.eval
, очевидно), но ты абсолютно прав,%q
кажется, помогает, не нарушая ничего, что я вижу. (Я не осознавал, что% q тоже будет выходить за квадратные скобки.) Еще одна проблема, которую я видел и исправляла, касалась пробелов. С тем жеa=(one "two " three)
, что и в вопросе Кигана: не толькоarray_contains a "two "
получил ложный отрицательный результат, но иarray_contains a two
получил ложный положительный. Достаточно легко исправить настройкойIFS
.eval _arr=( $(eval printf '[%q]="1"\ ' "\"\${$1[@]}\"") )
, и вы можете отказаться отlocal IFS=
. По-прежнему существует проблема с пустыми полями в массиве, поскольку Bash откажется создавать пустой ключ в ассоциативном массиве. Быстрый хакерский способ исправить это - добавить фиктивного персонажа, скажемx
:eval _arr=( $(eval printf '[x%q]="1"\ ' "\"\${$1[@]}\"") )
иreturn $(( 1 - 0${_arr[x$2]} ))
.Моя версия техники регулярных выражений, которая уже была предложена:
Здесь происходит то, что вы расширяете весь массив поддерживаемых значений в слова и добавляете конкретную строку, в данном случае «X-», к каждому из них и делаете то же самое для запрошенного значения. Если этот массив действительно содержится в массиве, то результирующая строка будет максимально соответствовать одному из полученных токенов, или, наоборот, ни одному из них. В последнем случае || оператор срабатывает, и вы знаете, что имеете дело с неподдерживаемым значением. До всего этого запрошенное значение удаляется из всех начальных и конечных пробелов посредством стандартных манипуляций со строками оболочки.
Я считаю, что он чистый и элегантный, хотя я не слишком уверен в его производительности, если ваш массив поддерживаемых значений особенно велик.
источник
Вот мой взгляд на эту проблему. Вот короткая версия:
И длинная версия, которая мне кажется намного проще на глазах.
Примеры:
источник
test_arr=("hello" "world" "two words")
?У меня был случай, когда мне нужно было проверить, содержится ли идентификатор в списке идентификаторов, сгенерированных другим скриптом / командой. У меня сработало следующее:
Вы также можете сократить / сжать это так:
В моем случае я запускал jq, чтобы отфильтровать JSON для списка идентификаторов, и мне пришлось позже проверить, был ли мой идентификатор в этом списке, и это сработало для меня лучше всего. Он не будет работать для созданных вручную массивов этого типа,
LIST=("1" "2" "4")
но для вывода сценариев, разделенных символом новой строки.PS .: не могу прокомментировать ответ, потому что я относительно новый ...
источник
Следующий код проверяет, находится ли данное значение в массиве, и возвращает его нулевое смещение:
Сопоставление выполняется для полных значений, поэтому установка VALUE = "three" не будет соответствовать.
источник
Это может стоить изучить, если вы не хотите повторять:
Фрагмент адаптирован с: http://www.thegeekstuff.com/2010/06/bash-array-tutorial/ Я думаю, что это довольно умно.
РЕДАКТИРОВАТЬ: Вы могли бы просто сделать:
Но последний работает, только если массив содержит уникальные значения. Поиск 1 в «143» даст ложное срабатывание, метинкс.
источник
Немного поздно, но вы можете использовать это:
источник
Я придумал этот, который работает только в zsh, но я думаю, что общий подход хорош.
Вы извлекаете свой шаблон из каждого элемента, только если он начинается
${arr[@]/#pattern/}
или заканчивается${arr[@]/%pattern/}
им. Эти две замены работают в Bash, но оба одновременно${arr[@]/#%pattern/}
работают только в zsh.Если измененный массив равен оригиналу, то он не содержит элемент.
Редактировать:
Этот работает в Bash:
После подстановки сравнивается длина обоих массивов. Очевидно, что если массив содержит элемент, подстановка полностью удалит его, и количество будет отличаться.
источник