bash: получить имя массива из параметра функции с сохранением индексов

bash: получить имя массива из параметра функции с сохранением индексов

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

getIndex() {
arrname=$1[@]
b=("${!arrname}")
index=1; while ((index<${#b[@]})); do
    if [[ "${b[$index]}" = "$VALUE" ]]; then
        echo "index is $index"; return
    fi  
        ((index++)); done

}

Но массив, имя которого я передаю этой функции, имеет индекс 1 как индекс первого элемента (мне это нужно, чтобы иметь индексы, аналогичные номерам строк, из которых я получаю шаблоны в массиве:

a=1
while read line; do
    if [[ $line =~ ^[0-9] ]]; then
        avg[$a]=`echo $line | awk '{print $6}'`
        ((a++));    
    fi

А если я запускаю функцию getIndex(), то первый элемент массива начинается с индекса 0.

Итак, вопрос: есть ли способ передать имя массива в параметре функции с сохранением индексов массива? Или, может быть, мне просто нужно забыть об этом и добавить +1 к ответу функции.

решение1

Вы используете эту конструкцию:

b=("${!arrname}")

Это расширяетценностимассива, создание нового массива b[]синдексы массива bash по умолчанию начинаются с 0. Чтобы правильно инициализировать копию массива, вам необходимо восстановить индексы (например, путем анализа или eval-ing вывода declare -p arrname)

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

Загвоздка (которая есть не всегда) в том, что !выполняет двойную функцию: его использование в косвенной адресации ${!name}несовместимо с его использованием для расширения индексов массива ${!arrname[@]}, поэтому нам приходится использовать eval.

Вот модифицированная версия, реализующая это:

getIndex2() {
  local arrname=$1 iidx idxs index ival val
  printf -v iidx '"${!%s[@]}"' "$arrname"
  eval "idxs=($iidx)"
  for index in "${idxs[@]}"; do
    printf -v ival '${%s[%s]}' "$arrname" "$index"
    eval "val=$ival"
    if [[ "${val}" = "$VALUE" ]]; then
        echo "index is $index"; return   
    fi
  done
}

Обратите внимание на использование printf -v var ...(bash-3.1+) для сохранения evalчитаемости. Индексы развернуты в массив, это не обязательно, плоский список тоже подойдет.

Смотрите такжеBashFAQ/006.

решение2

Массивы в Bash начинаются с нуля. Они просто есть. Тем не менее, если вызнатьчто массив всегда будет индексироваться числами, начиная с 1, просто добавьте +1 к результату. Добавьте второй аргумент к функции, который сообщит ей, с какого числа начинать или какое число добавлять к результату, и добавьте разумное значение по умолчанию для случаев, когда второй аргумент отсутствует. Или выполните цикл по индексам какг-н.spuraticпредполагает.

Связанный контент