функция, которая проверяет, содержит ли массив с указанным именем указанное значение - PullRequest
1 голос
/ 23 апреля 2020

Как написать функцию, in_array, которая проверяет, содержит ли указанный массив указанное значение

Функция должна принимать два аргумента:

  1. array_name - имя массива
  2. value - значение для проверки

С помощью этого тестового жгута:

colors=(red green yellow "royal blue")

test() {
  local answer=no
  if in_array colors "$1"; then
    answer=yes
  fi
  printf "%-13s  %s\n" "$1" "$answer"
}

test red
test green
test "royal blue"
test blue

должен быть получен следующий вывод:

red            yes
green          yes
royal blue     yes
blue           no

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

Ответы [ 4 ]

2 голосов
/ 23 апреля 2020

Вот один из способов.

#!/usr/bin/env bash

inarray() {
    local n=$1 h
    shift
    for h; do
      [[ $n = "$h" ]] && return
    done
    return 1
}


colors=(red green yellow "royal blue")

test() {
  local answer=no
  if inarray  "$1" "${colors[@]}"; then
    answer=yes
  fi
  printf "%-13s  %s\n" "$1" "$answer"
}
test red
test green
test "royal blue"
test blue
2 голосов
/ 23 апреля 2020
# bash 4.3+
in_array() {
  local -n a=$1
  # IFS must be set to a character guaranteed not to exist in the 
  # array values; otherwise a false positive could occur.
  # A very safe default has been chosen: non-printable character 0x1F.
  # The IFS character may be specified as an optional 3rd argument.
  local IFS=${3:-$'\x1F'}
  [[ "$IFS${a[*]}$IFS" = *"$IFS$2$IFS"* ]] || return 1
}

Использовались следующие методы:

  1. ссылка на имя (Bash 4.3)
  2. метод поиска по массиву, описанный здесь: { ссылка }
  3. IFS объявлен как local, поэтому нет необходимости восстанавливать его значение

ОБНОВЛЕНИЕ: Вот вариант без зависимости от Bash 4,3 ссылки на имя особенность. Вместо этого он использует неясный (недокументированный?) Синтаксис, который, очевидно, работает еще до Bash 3. Для получения дополнительной информации об этом синтаксисе см. "трюк # 2" при https://mywiki.wooledge.org/BashFAQ/006

# bash 3+
in_array() {                                                     
  local name="$1[*]"                                                                                                          
  local IFS=${3:-$'\x1F'}                                              
  [[ "$IFS${!name}$IFS" = *"$IFS$2$IFS"* ]] || return 1                
}                                                                      
0 голосов
/ 28 апреля 2020

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

#  in_values VAL VAL1 VAL2 ... VALN
#
#  Tests if VAL equals one of the following values
#
in_values() {
  local v=$1
  while shift; do 
    [[ $v == "$1" ]] && return 0
  done
  return 1
}

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

Пример использования:

rgb=(RED GREEN BLUE)
cmyk=(CYAN MAGENTA YELLOW BLACK)
while true; do
  echo -n "Enter the color of the ink you need or 'NONE': " 
  read -i NONE color
  if in_values "$REPLY" "${rgb[@]}" "${cmyk[@]}" NONE; then
    break
  else
    echo "validation error, please try again"
  fi
done
if in_values "$color" "${rgb[@]}"; then
  echo "Order ink cartridge RGB59XL"
elif in_values "$color" "${cmyk[@]}"; then
  echo "Order ink cartridge CMYK64"
fi
0 голосов
/ 23 апреля 2020

Как-то так

colors=(red green yellow "royal blue")

in_array () {
    name="$1[@]"
    value="$2"
    for item in "${!name}"; { [[ $value =~ $item ]] && { printf -v answer "yes"; break; } || printf -v answer "no"; }
    echo $answer
}

Тестирование

$ in_array colors test
no

$ in_array colors green
yes

$ in_array colors royal
no

$ in_array colors 'royal blue'
yes

Обновление с улучшенной версией этого

in_array () {
    name="$1[@]"
    value="$2"
    printf -v re '|%s' "${!name}"
    [[ $value =~ ${re:1} ]] && printf "yes" || printf "no"
}

Или с возвратом

[[ $value =~ ${re:1} ]] && return 0 || return 1
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...