Доброго дня!
В даній статті висвітлено синтаксис і приклад створення функцій в командному інтерпретаторі Bash.
Синтаксис
Shell-функція являється об'єктом, який викликається як звичайна команда і виконує складені команди з новим набором позиційних параметрів (параметрів скрипта командного рядка). Функції оголошуються наступним чином:
[ function ] ім'я_функції () складена-команда [перенаправлення]
В квадратних дужках вказуються необов'язкові елементи; в іменах функцій і змінних не можна використовувати апострофи.
Даний синтаксис визначає нову функцію з назвою “ім'я_функції”. Зарезервоване слово “function” являється необов'язковим. Якщо вказане зарезервоване слово “function”, дужки являються необов'язковими. Тіло функції являється складеною командою “складена-команда” (яка може розміщуватися між “()”, “(())”, “[]”, “[[]]”, “{}”, операторами вибору і циклів, а також оператором select). Зазвичай, ця команда являється списком команд, які розміщені між фігурними дужками “{}”, але також вони можуть розміщуватись і між іншими операторами. Команди з списку “складена-команда” виконуються у випадку вказування імені “ім'я_функції” в якості ім'я простої команди. Будь-які перенаправлення потоків, які вказані під час оголошення функції, виконуються під час її виконання. Статус виходу операції оголошення функції являється нульовим, окрім випадків синтаксичних помилок або оголошень функцій з однаковим іменем. Після виклику функції, статус виходу являється статусом виходу останньої команди виконаної в її тілі.
Функції виконуються в контексті поточного командного інтерпретатора; для її інтерпретування не створюється новий процес. Під час виклику функції, аргументи до функції стають позиційними параметрами під час її виконання. Спеціальний параметр “#” оновлюється для відображення відповідних змін. Спеціальний параметр “0” залишається незмінним. Перший елементи змінної FUNCNAME встановлюється у ім'я функції під час її виконання. Усі інші аспекти виконання командного рядка є ідентичними між функцією і її клієнтом, окрім пасток DEBUG і RETURN, які не успадковуються від клієнта, якщо у неї не встановлений атрибут trace або не увімкнений прапорець -o functrace за допомогою вбудованої команди set.
Локальні змінні функції можуть бути оголошеними за допомогою вбудованої команди local. Зазвичай, змінні і їхні значення розділяються між функцією і її клієнтом (скриптом загалом).
Якщо використана вбудована команда return виконана всередині тіла функції, функція завершує своє виконання і управління передається наступній команді, яка стоїться після місця виклику функції. Будь-яка команда, яка асоціюється з пасткою RETURN, виконується перед продовженням виконання скрипта. Під час завершення виконання функції, значення позиційних параметрів і спеціального параметру “#” відновлюється до тих, які вони мали перед викликом функції.
Імена функції і їх оголошення можуть бути переглянутими за допомогою команди declare або typeset з переданим їм прапорцем -f. Прапорець -F, переданий до команди declare або typeset спричинить дані команди відобразити тільки імена функцій (і файл, в якому вони були оголошеними, якщо увімкнена опція командного рядка extdebug). Функції можуть бути експортованими до дочірніх процесів командного інтерпретатора за допомогою вбудованої команди export з прапорцем -f. Функцію можна знищити за допомогою вбудованої команди unset з переданим прапорцем -f. Зауважте, що shell-функції і змінні з однаковим ім'ям можуть спричинити існування множинних об'єктів, переданих до потомків командного інтерпретатора. Це необхідно враховувати у місцях, де така ситуація може спричинити проблеми.
Функції можуть бути рекурсивними. Жодні обмеження не накладаються на кількість рекурсивних викликів.
Приклад
Розглянемо приклад який імітує виконання стандартної Юнікс-команди find:
find ./ -writable
На чому ще вивчати мову програмування, якщо не на велосипедах? Дана команда (з вказаними параметрами) шукає в поточній директорії і її дочірніх директоріях файли і директорії, які поточному користувачу дозволено перезаписувати. Вміст такого скрипта може бути наступним:
#!/bin/bash
# оголошуємо функцію, яка виводить на екран файли,
# які поточному користувачу дозволено перезаписувати
print_writtable_files ()
{
# оголошуємо локальну змінну функції
# для утримування і зміни значення шляху
local dpath=$1
# перевіряємо чи існує директорія
# якщо ні - завершуємо виконання функції
if [[ ! -d $dpath ]]
then
echo "файл $dpath не існує, або не являється директорією"
return 1
fi
# якщо в кінці шляху до директорії немає слешу
# - додаємо його
if [[ ! $dpath = */ ]]; then
dpath="$dpath"/
fi
# отримуємо усі файли в директорії за шляхом dpath
# і тестуємо кожен отриманий елемент на те що він є:
# - aбо директорія (рекурсія для її тестів)
# - або файл, у який можна виконувати запис
for fname in "$dpath"*
do
# якщо шлях fname являється символічним посиланням
# - не тестуємо його
if [[ -h "$fname" ]] ; then continue ; fi
# тестуємо файл на можливість запису
# якщо він доступний для запису -
# виводимо його шлях на екран
if [[ -w "$fname" ]] ; then
echo "$fname"
fi ;
# якщо за шляхом fname знаходиться директорія
# рекурсивно викликаємо дану функцію,
# щоб протестувати усю файлову систему
# відносно початкової директорії
if [[ -d "$fname" ]] ; then
print_writtable_files "$fname"
fi ;
done ;
}
# перевіряємо чи користувач передав валідний шлях до директорії
# якщо ні - завершуємо роботу скрипта
if [[ ! -d "$1" ]] ; then
echo "Шлях '$1' не являється валідним параметром для скрипта."
echo "Передайте шлях до існуючої директорії"
exit 1
fi
# отримуємо абсолютний канонічний шлях до вказаної директорії
testdpath=$(readlink -f "$1")
# виклик функції, яка рекурсивно тестує файли і директорії
print_writtable_files "$testdpath"
В загальному скрипт оголошує функцію print_writtable_files, перевіряє валідність шляху до директорії, яку користувач бажає протестувати на доступні для запису файли і директорії, після чого передає абсолютний шлях до цієї директорії в оголошену функцію. Більш детально в коментаріях. Виконання даного скрипта може виглядати наступним (скрипт знаходиться у файлі function.sh):