В цьому розділі ми обговоримо:

  • що таке функції
  • створення та показ функцій з командного рядка
  • функції в сценаріях
  • передачу аргументів у функції
  • коли вживати функції

Вступ

Що таке функції?

Функції оболонки – це спосіб групування команд для подальшого виконання, задаючи одне ім’я такій групі або підпрограмі. Ім’я підпрограми повинне бути унікальним всередині сценарію чи оболонки. Всі команди, що утворюють функцію, виконуються як звичайно. Коли функція викликається як проста команда, виконується список команд, асоційованих з заданою функцією. Функції виконуються всередині оболонки, у котрій вони були оголошені; для виконання команд нового процесу не створюється.

Перед пошуком функцій переглядаються вбудовані команди оболонки – break, :, ., continue, eval, exec, exit, export, readonly, return, set, shift, trap і unset.

Синтаксис функцій

Для оголошення функцій використовується такий синтаксис

function НАЗВА_ФУНКЦІЇ {КОМАНДИ;}

або такий

НАЗВА_ФУНКЦІЇ () {КОМАНДИ;}

Обидва оголошують нову функцію оболонки. Вживання оператора function не є обов’язковим, але за його відсутності після імені функції повинні стояти круглі дужки. Команди, що перелічені у фігурних дужках, утворюють тіло функції. Ці команди виконуються щоразу, коли описана функція викликається як звичайна команда. Стан завершення функції рівний станові завершення останньої команди, виконаної в її тілі.

Увага: Поширені помилки.

Фігурні дужки повинні бути відділеними від тіла функції, в іншому випадку вони невірно інтерпретуються.

Тіло функції повинно завершуватись крапкою з комою або символом нового рядка.

Позиційні параметри у функціях

Функції схожі на міні-сценарії: вони можуть приймати параметри, вони можуть використовувати локальні змінні (створені за допомогою вбудованої команди local) і вони можуть повертати значення у оболонку, що їх викликала.

Функції також мають систему інтерпретації позиційних параметрів. Проте, позиційні параметри у функціях не ідентичні тим, що передаються у сценарії.

Під час виконання функції її аргументи стають позиційними параметрами. Особливий параметр # розкривається у кількість позиційних параметрів. Позиційний параметр 0 є незмінним. Змінна Bash FUNCNAME встановлюється в ім’я функції під час виконання останньої.

Якщо всередині функції викликається вбудована команда return, виконання функції переривається і управління переходить на наступну після імені функції команду. Коли функція завершується, позиційні параметри та особливий параметр # приймають ті значення, які вони мали до виклику функції. Якщо команді return був переданий числовий аргумент, він стає станом завершення функції. Простенький приклад:

[lydia@cointreau ~/test] cat showparams.sh
#!/bin/bash

echo "Цей сценарій демонструє аргументи функцій."
echo

echo "Першим позиційним параметром для сценарію є $1."
echo

test ()
{
echo "Першим позиційним параметром для функції є $1."
RETURN_VALUE=$?
echo "Станом завершення функції є $RETURN_VALUE."
}

test other_param

[lydia@cointreau ~/test] ./showparams.sh parameter1
Цей сценарій демонструє аргументи функцій.

Першим позиційним параметром для сценарію є parameter1.

Першим позиційним параметром для функції other_param.

Станом завершення функції є 0.

[lydia@cointreau ~/test]

Зверніть увагу, що стан завершення функції завжди зберігається у змінній, то ж ви можете звернутись до нього пізніше. Стартові сценарії у вашому комп’ютері часто застосовують техніку аналізу змінної RETVAL у умовних перевірках, наприклад ось так:

if [ $RETVAL -eq 0 ]; then
  <start the daemon>

Або як у цьому прикладі з сценарію /etc/init.d/amd, де вжито оптимізовані можливості Bash:

[ $RETVAL = 0 ] && touch /var/lock/subsys/amd

Команда після && буде виконана лише в тому випадку, якщо результатом перевірки буде істина; це – коротший спосіб вжитку структури if/then/fi. Стан завершення функції часто вживається як стан завершення цілого сценарію. Ви можете побачити багато стартових сценаріїв, які закінчуються командою exit $RETVAL.

Відображення функцій

Всі функції, що відомі у поточній оболонці, можуть бути показані за допомогою вбудованої команди set, викликаної без опцій. Функції зберігаються після завершення роботи, поки вони не будуть видалені командою unset. Команда which також показує функції:

[lydia@cointreau ~] which zless
zless is a function
zless ()
{
    zcat "$@" | "$PAGER"
}

[lydia@cointreau ~] echo $PAGER
less

Це вид функцій, які типово сконфігуровані у користувацьких стартових файлах оболонки. Функції є значно гнучкішими, аніж файли, і пропонують зручний і простий спосіб адаптування користувацького середовища. Ось ще один приклад для користувачів DOS:

dir ()
{
   ls -F --color=auto -lF --color=always "$@" | less -r
}

Приклади функцій у сценаріях

Повторна обробка

У вашій системі є велика кількість сценаріїв, яка використовує функції як структурований шлях обробки серій команд. На деяких Linux системах, наприклад, ви знайдете файл означень /etc/rc.d/init.d/functions, який включено до всіх стартових сценаріїв. Вживаючи такий метод, стандартні задачі, такі як перевірка запущеного процесу, запуск та зупинка демона й інші повинні бути описані лише один раз. Якщо така задача повинна виконатись ще раз, код виконується повторно. Ось функція checkpid з цього файлу:

# Перевіряє, чи запущено $pid (їх може бути кілька)
checkpid() {
       local i

       for i in $* ; do
               [ -d "/proc/$i" ] && return 0
       done
       return 1
}

Дана функція використовується в цьому ж сценарії в інших функціях, а також в інших сценаріях. Функція daemon, наприклад, використовується у більшості стартових сценаріїв щоб запустити серверні процеси.

Установка шляхів

Така секція може бути у вашому файлі /etc/profile. Функція pathmunge використовується щоб задати шляхи для користувача root та інших.

pathmunge () {
        if ! echo $PATH | /bin/egrep -q "(^|:)$1($|:)" ; then
           if [ "$2" = "after" ] ; then
              PATH=$PATH:$1
           else
              PATH=$1:$PATH
           fi
        fi
}

# Path manipulation
if [ `id -u` = 0 ]; then
        pathmunge /sbin
        pathmunge /usr/sbin
        pathmunge /usr/local/sbin
fi

pathmunge /usr/X11R6/bin after

unset pathmunge

Функція приймає свій перший аргумент як шлях до файлів. Якщо такого шляху немає у змінній path, він додається до неї. Другий аргумент визначає, куди додавати заданий шлях – на початок чи у кінець списку.

Звичайні користувачі зазвичай мають лише /usr/X11R6/bin у своєму списку шляхів, тоді як суперкористувач має доступ до деяких каталогів із системними командами.

Після використання функція видаляється і стає недоступною.

Видалення резервних копій

Наступний сценарій є одним з тих, що я використовую для резервування файлів моїх книжок. Він використовує ключі ssh для віддалених з’єднань. У ньому описуються дві функції – buplinux і bupbash, кожна з котрих робить .tar-файл, який потім стискається та відправляється на віддалений сервер, після чого локальна копія видаляється.

В неділю виконується лише bupbash.

#/bin/bash

LOGFILE="/nethome/tille/log/backupscript.log"
echo "Розпочато резервування `date`" >> "$LOGFILE"

buplinux()
{
DIR="/nethome/tille/xml/db/linux-basics/"
TAR="Linux.tar"
BZIP="$TAR.bz2"
SERVER="rincewind"
RDIR="/var/www/intra/tille/html/training/"

cd "$DIR"
tar cf "$TAR" src/*.xml src/images/*.png src/images/*.eps
echo "Стискання $TAR..." >> "$LOGFILE"
bzip2 "$TAR"
echo "...done." >> "$LOGFILE"
echo "Копіювання до $SERVER..." >> "$LOGFILE"
scp "$BZIP" "$SERVER:$RDIR" > /dev/null 2>&1
echo "...done." >> "$LOGFILE"
echo -e "Резервування Linux cource завершено:\nДжерельні файли, малюнки PNG та EPS.\nТимчасові файли видалено." >> "$LOGFILE"
rm "$BZIP"
}

bupbash()
{
DIR="/nethome/tille/xml/db/"
TAR="Bash.tar"
BZIP="$TAR.bz2"
FILES="bash-programming/"
SERVER="rincewind"
RDIR="/var/www/intra/tille/html/training/"

cd "$DIR"
tar cf "$TAR" "$FILES"
echo "Compressing $TAR..." >> "$LOGFILE"
bzip2 "$TAR"
echo "...done." >> "$LOGFILE"
echo "Copying to $SERVER..." >> "$LOGFILE"
scp "$BZIP" "$SERVER:$RDIR" > /dev/null 2>&1
echo "...done." >> "$LOGFILE"

echo -e "Резервування Bash course завершено:\n$FILES\nТимчасові файли видалено." >> "$LOGFILE"
rm "$BZIP"
}

DAY=`date +%w`

if [ "$DAY" -lt "2" ]; then
  echo "Сьогодні `date +%A`, резервується лише Bash course." >> "$LOGFILE"
  bupbash
else
  buplinux
  bupbash
fi

echo -e "Віддалене резервування `date` завершено.\n----------" >> "$LOGFILE"

Сценарій запускається за допомогою cron, тобто без взаємодії з користувачем, то ж ми перенаправляємо повідомлення про помилки від команди scp у /dev/null. Можна зауважити, що окремі кроки можна було б об’єднати наприклад ось так:

tar c dir_to_backup/ | bzip2 | ssh server "cat > backup.tar.bz2"

Але якщо ви зацікавлені у можливості створення проміжних результатів, які могли б бути відновлені у разі збоїв у роботі сценарію, то ви не станете так робити.

Підсумок

Функції пропонують зручний спосіб групування команд, які ви хочете виконувати регулярно. Під час їх запуску позиційні параметри відповідно змінюються. Після завершення вони приймають свої попередні значення. Функції схожі на маленькі сценарії, і, як і сценарії, вони генерують числовий код стану завершення роботи. Не зважаючи на те, що це маленький розділ, він містить важливі знання, котрі допоможуть вам досягти того стану лінощів, що є характерним для системних адміністраторів.

Вправи

Ось деякі важливі речі, які ви можете зробити за допомогою функцій

  1. Додайте до вашого файлу ~/.bashrc функцію, що автоматизує роздрук сторінок довідки. Результатом повинна бути можливість ввести щось на кшталт printman <команда> після чого роздруковані сторінки довідки починають вилазити з вашого принтера. Перевірте роботу функції на псевдо принтері. Додатково зробіть можливість задати номер секції, з котрої видруковувати довідку.
  2. Створіть підкаталог у вашому домашньому каталозі, де ви будете зберігати реалізації функцій. Запишіть туди кілька функцій. Серед інших там можуть бути функції ідентичні командам DOS чи комерційних версій UNIX, якщо ви працюєте з Linux, або ж навпаки. Згодом ці функції можна буде імпортувати у вашу оболонку з допомогою файлу ~/.bashrc.