Практическое написание сценариев командной оболочки Bash/Эмуляция ссылочной адресации: различия между версиями

Содержимое удалено Содержимое добавлено
Нет описания правки
Строка 299:
Max value: 12
</source>
 
Как мы уже показали в предыдущем примере, можно делать ссылки на массивы и на их отдельные элементы. К сожалению по таким ссылкам можно только читать данные, что очень ограничивает их применение.
 
<source lang=bash>
declare -a ARRAY_STRS=('John Doe' 'Bill Watson') # Простой массив
declare -A PERSON=([name]="John" [age]=23) # Ассоциативный массив
 
# Ссылка на простой массив выглядит так, при этом вариации 'declare -n' для массивов нет
REF_1=ARRAY_STRS[@] # Ссылка на массив
declare REF_1=ARRAY_STRS[@] # Допустимо
 
# Следующий вариант формально является ссылкой на массив, но он НЕ РЕКОМЕНДУЕТСЯ из-за нежелательного побочного эффекта,
# связанного с механизмом word splitting
WRONG_REF_1=ARRAY_STRS[*]
 
# На отдельный элемент простого массива ссылка создается похожим образом, но нужно указать индекс
REF_EL_1=ARRAY_STRS[1] # Ссылка на элемент с индексом 1
declare REF_EL_1=ARRAY_STRS[1] # Допустимо
 
# В ассоциативных массивах обычно ссылаются на конкретные элементы
REF_NAME=PERSON[name]
REF_AGE=PERSON[age]
 
# Разрешаются ссылки на массива и их элементы все тем же образом
echo "${!REF_1}" # John Doe Bill Watson
echo "${!WRONG_REF_1}" # John Doe Bill Watson
echo "${!REF_EL_1}" # Bill Watson
echo "${!REF_NAME}" # John
echo "${!REF_AGE}" # 23
</source>
 
Ссылка на простой массив на практике часто используется в связке с функцией <code>printf</code> из-за очень интересного побочного эффекта: можно распечатать все элементы массива, используя единый формат для каждого элемента, не прибегая к циклу. Например
<source lang=bash>
printf "<%s> " "${!REF_1}"; echo # Вот так можно распечатать все элементы массива, применяя один формат к каждому элементу
</source>
Результат
<source lang=bash>
<John Doe> <Bill Watson>
</source>
 
Ссылки на отдельные элементы простых массивов можно использовать для организации произвольных переборов, если вместо конкретного индекса использовать произвольный счетчик. Это показано в следующем примере.
<source lang=bash>
#!/bin/bash
 
declare -a ARRAY_STRS=('John Doe' 'Bill Watson' 'Bart Simpson' 'Bugs Bunny' 'Homer Simpson') # Простой массив
declare -a NUMS=({-10..10}) # Массив, сгенерированный через скобочную подстановку (см. в след. главе)
 
# Следующая функция печатает элементы простого массива в определенном формате, начиная с определенного элемента (если указано во-втором аргументе)
# или с начала
print_from() {
local ref_element=${1}[index]
local index=${2:-0}
while printf "<%s> " "${!ref_element}"; ((index++)) ; [[ -n ${!ref_element+_} ]]; do true; done
echo
}
 
print_from ARRAY_STRS
print_from ARRAY_STRS 2
 
print_from NUMS
print_from NUMS 6
</source>
Результат
<source lang=bash>
<John Doe> <Bill Watson> <Bart Simpson> <Bugs Bunny> <Homer Simpson> # с начала
<Bart Simpson> <Bugs Bunny> <Homer Simpson> # с третьего элемента
<-10> <-9> <-8> <-7> <-6> <-5> <-4> <-3> <-2> <-1> <0> <1> <2> <3> <4> <5> <6> <7> <8> <9> <10> # с начала
<-4> <-3> <-2> <-1> <0> <1> <2> <3> <4> <5> <6> <7> <8> <9> <10> # с седьмого элемента
</source>
 
Обратите внимание как функция объявляет универсальную ссылку на элемент массива. Для этого мы используем счетчик <code>index</code>, который увеличивается в цикле и двигает ссылку как итератор по массиву. Изменяя начальное значение для <code>index</code>, мы определяем с какого элемента начать. Чтобы цикл остановился, мы используем хитрое условие <code>[[ -n ${!ref_element+_} ]]</code>, которое возвращает ИСТИНУ всякий раз, когда ссылку удается раскрыть.
 
{{Нижняя автоматическая навигация|next=Bash подстановки|prev=Функции}}