105.2 Урок 1
Сертифікат: |
LPIC-1 |
---|---|
Версія: |
5.0 |
Розділ: |
105 Оболонки та сценарії оболонки |
Тема: |
105.2 Налаштування або написання простих сценаріїв |
Урок: |
1 з 2 |
Вступ
Середовище оболонки Linux дозволяє використовувати файли, які називаються сценарії, що містять команди з будь-якої доступної програми в системі в поєднанні з вбудованими командами оболонки для автоматизації завдань користувача та/або системи. Дійсно, багато завдань обслуговування операційної системи виконуються сценаріями, що складаються з послідовностей команд, структур прийняття рішень та циклів з умовами. Хоча сценарії здебільшого призначені для завдань, пов’язаних із самою операційною системою, вони також корисні для орієнтованих на користувача завдань, таких як масове перейменування файлів, збір і синтаксичний аналіз даних або будь-які інші дії командного рядка, що повторюються.
Сценарії — це не що інше, як текстові файли, які поводяться як програми. Актуальна програма, інтерпретатор, читає та виконує інструкції, перелічені у файлі сценарію. Інтерпретатор також може розпочати інтерактивний сеанс, у якому команди, включаючи сценарії, зчитуються та виконуються під час їх введення, як у випадку з сеансами оболонки Linux. Файли сценаріїв можуть групувати ці інструкції та команди, коли вони стають надто складними, щоб їх можна було реалізувати як псевдонім або спеціальну функцію оболонки. Крім того, файли сценаріїв можна підтримувати як звичайні програми, і, оскільки це просто текстові файли, їх можна створювати та змінювати за допомогою будь-якого простого текстового редактора.
Структура та виконання сценарію
По суті, файл сценарію — це впорядкована послідовність команд, які мають виконуватися відповідним інтерпретатором команд. Те, як інтерпретатор читає файл сценарію, різниться, і існують різні способи зробити це в сеансі оболонки Bash, але інтерпретатором за замовчуванням для файлу сценарію буде той, що вказано в першому рядку сценарію відразу після символів \# !
(відомий як shebang). У сценарії з інструкціями для оболонки Bash перший рядок має бути !/bin/bash
. Якщо вказати цей рядок, інтерпретатор для всіх інструкцій у файлі буде інтерпретатором /bin/bash
. За винятком першого рядка, усі інші рядки, що починаються з решетки , ігноруватимуться, тому їх можна використовувати для розміщення нагадувань і коментарів. Порожні рядки також ігноруються. Тому дуже лаконічний файл сценарію оболонки можна записати так:
#!/bin/bash # A very simple script echo "Cheers from the script file! Current time is: " date +%H:%M
Цей сценарій містить лише дві інструкції для інтерпретатора /bin/bash
: вбудовану команду echo
і команду date
. Найпростіший спосіб запустити файл сценарію — це запустити інтерпретатор із шляхом до сценарію в якості аргументу. Отже, якщо припустити, що попередній приклад було збережено у файлі сценарію під назвою script.sh
у поточному каталозі, його буде прочитано та інтерпретовано Bash за допомогою такої команди:
$ bash script.sh Cheers from the script file! Current time is: 10:57
Команда echo
автоматично додасть новий рядок після відображення вмісту, але параметр -n
відмінить цю поведінку. Таким чином, використання echo -n
у сценарії змусить виведення обох команд відображатися в одному рядку:
$ bash script.sh Cheers from the script file! Current time is: 10:57
Хоча це не обов’язково, суфікс .sh
допомагає ідентифікувати сценарії оболонки під час виведення їх у списку та пошуку файлів.
Tip
|
Bash викличе будь-яку команду, зазначену після |
Якщо файл сценарію призначений для виконання іншими користувачами в системі, важливо перевірити, чи встановлено належні дозволи на читання. Команда chmod o+r script.sh
надасть дозвіл на читання всім користувачам у системі, дозволяючи їм виконувати script.sh
, вказавши шлях до файлу сценарію як аргумент команди bash
. Крім того, у файлі сценарію може бути встановлено дозвіл на біт виконання, щоб файл можна було виконати як звичайну команду. Біт виконання активується у файлі сценарію командою chmod
:
$ chmod +x script.sh
Якщо біт виконання ввімкнено, файл сценарію під назвою script.sh
у поточному каталозі можна виконати безпосередньо за допомогою команди ./script.sh
. Сценарії, розміщені в каталозі, зазначеному у змінній середовища PATH
, також будуть доступні без повного шляху.
Warning
|
Сценарій, який виконує обмежені дії, може мати активований дозвіл SUID, тому звичайні користувачі також можуть запускати сценарій із привілеями root. У цьому випадку дуже важливо переконатися, що жоден користувач, крім root, не має дозволу на запис у файлі. В іншому випадку звичайний користувач може змінити файл для виконання довільних і потенційно шкідливих операцій. |
Розміщення та відступи команд у файлах сценаріїв не надто жорсткі. Кожен рядок у сценарії оболонки виконуватиметься як звичайна команда оболонки в тій самій послідовності, що й рядок у файлі сценарію, і ті самі правила, що застосовуються до підказки оболонки, також застосовуються до кожного рядка сценарію окремо. Можна розмістити дві або більше команд в одному рядку, розділених крапкою з комою:
echo "Cheers from the script file! Current time is:" ; date +%H:%M
Хоча цей формат іноді може бути зручним, його використання необов’язкове, оскільки послідовні команди можна розміщувати по одній команді в рядку, і вони виконуватимуться так само, як вони були розділені крапкою з комою. Іншими словами, крапку з комою можна замінити символом нового рядка у файлах сценарію Bash.
Коли сценарій виконується, команди, що містяться в ньому, не виконуються безпосередньо в поточному сеансі, а замість цього вони виконуються новим процесом Bash, який називається sub-shell. Це запобігає перезапису сценарієм змінних середовища поточного сеансу та залишенню неконтрольованих змін у поточному сеансі. Якщо мета полягає в тому, щоб запустити вміст сценарію в поточному сеансі оболонки, тоді його слід виконати за допомогою source script.sh
або . script.sh
(зверніть увагу, що між крапкою та назвою сценарію є пробіл).
Як це буває під час виконання будь-якої іншої команди, підказка оболонки знову буде доступною лише тоді, коли сценарій завершить своє виконання, а його код статусу виходу буде доступний у змінній $?
. Щоб змінити цю поведінку, щоб поточна оболонка також завершувалася, коли завершується сценарій, сценарію або будь-якій іншій команді може передувати команда exec
. Ця команда також замінить код статусу виходу поточного сеансу оболонки на власний.
Змінні
Змінні в сценаріях оболонки поводяться так само, як і в інтерактивних сеансах, якщо інтерпретатор той самий. Наприклад, формат SOLUTION=42
(без пробілів навколо знака рівності) призначить значення 42
змінній з назвою SOLUTION
. Згідно з домовленістю, для імен змінних використовуються великі літери, але це не є обов’язковим. Однак назви змінних не можуть починатися з неалфавітних символів.
На додаток до звичайних змінних, створених користувачем, сценарії Bash також мають набір спеціальних змінних, які називаються параметри. На відміну від звичайних змінних, імена параметрів починаються з неалфавітного символу, який позначає його функцію. Аргументи, передані сценарію, та інша корисна інформація зберігаються в таких параметрах, як $0
, $*
, $?
тощо, де символ після знака долара вказує на інформацію, яку потрібно отримати:
$*
-
Всі аргументи передані в скрипт.
$@
-
Всі аргументи передані в скрипт. Якщо використовуються подвійні лапки, наприклад,
"$@"
, кожен аргумент буде взято в подвійні лапки. $#
-
Кількість аргументів.
$0
-
Ім’я файлу сценарію.
$!
-
PID останньої виконаної програми.
$$
-
PID поточної оболонки.
$?
-
Числовий код статусу виходу останньої виконаної команди. Для стандартних процесів POSIX числове значення
0
означає, що остання команда була успішно виконана. Це також стосується сценаріїв оболонки.
Позиційний параметр — це параметр, позначений однією або декількома цифрами, окрім однієї цифри 0
. Наприклад, змінна $1
відповідає першому аргументу, наданому скрипту (позиційний параметр один), $2
відповідає другому аргументу і так далі. Якщо позиція параметра більша за дев’ять, на нього потрібно посилатися у фігурних дужках, як у ${10}
, ${11}
тощо.
З іншого боку, звичайні змінні призначені для зберігання вставлених вручну значень або результатів, створених іншими командами. Команда read
, наприклад, може бути використана всередині сценарію, щоб попросити користувача ввести дані під час виконання сценарію:
echo "Do you want to continue (y/n)?" read ANSWER
Повернене значення буде збережено в змінній ANSWER
. Якщо ім’я змінної не вказано, за умовчанням використовуватиметься ім’я змінної REPLY. Також можна використовувати команду read
для читання кількох змінних одночасно:
echo "Type your first name and last name:" read NAME SURNAME
У цьому випадку кожне слово, розділене пробілами, буде присвоєно змінним NAME
та SURNAME
відповідно. Якщо кількість заданих термінів перевищує кількість змінних, то терміни, що перевищують, будуть збережені в останній змінній. Сам read
може відобразити повідомлення користувачеві за допомогою параметра -p
, що робить команду echo
зайвою в цьому випадку:
read -p "Type your first name and last name:" NAME SURNAME
Сценарії, що виконують системні завдання, часто потребують інформації, наданої іншими програмами. Зворотне позначення можна використовувати для збереження результату команди в змінній:
$ OS=`uname -o`
У цьому прикладі результат команди uname -o
зберігатиметься в змінній OS
. Ідентичний результат буде отримано за допомогою $()
:
$ OS=$(uname -o)
Довжина змінної, тобто кількість символів, які вона містить, повертається шляхом додавання символу # перед назвою змінної. Ця функція, однак, вимагає використання синтаксису фігурних дужок для позначення змінної:
$ OS=$(uname -o) $ echo $OS GNU/Linux $ echo ${#OS} 9
Bash також містить одновимірні масиви змінних, тому набір пов’язаних елементів можна зберігати з одним іменем змінної. Кожен елемент у масиві має числовий індекс, який потрібно використовувати для запису та читання значень у відповідному елементі. На відміну від звичайних змінних, масиви мають бути оголошені за допомогою вбудованої команди Bash declare
. Наприклад, щоб оголосити змінну з назвою SIZES
як масив:
$ declare -a SIZES
Масиви також можуть бути неявно оголошені, коли вони заповнюються з попередньо визначеного списку елементів, використовуючи нотацію в дужках:
$ SIZES=( 1048576 1073741824 )
У прикладі два великих цілих значення зберігаються в масиві SIZES. На елементи масиву потрібно посилатися за допомогою фігурних і квадратних дужок, інакше Bash не змінить або не відобразить елемент правильно. Оскільки індекси масиву починаються з 0, вміст першого елемента знаходиться в ${SIZES[0]}
, другого елемента в ${SIZES[1]}
і так далі:
$ echo ${SIZES[0]} 1048576 $ echo ${SIZES[1]} 1073741824
На відміну від читання, зміна вмісту елемента масиву виконується без фігурних дужок (наприклад, SIZES[0]=1048576
). Як і зі звичайними змінними, довжина елемента в масиві повертається з символом решітки (наприклад, ${#SIZES[0]}
для довжини першого елемента в масиві SIZES
). Загальна кількість елементів у масиві повертається, якщо @
або *
використовуються як індекс:
$ echo ${#SIZES[@]} 2 $ echo ${#SIZES[*]} 2
Масиви також можуть бути оголошені, використовуючи вихідні дані команди як початкові елементи за допомогою підстановки команд. У наступному прикладі показано, як створити масив Bash, елементи якого є підтримуваними файловими системами поточної системи:
$ FS=( $(cut -f 2 < /proc/filesystems) )
Команда cut -f 2 < /proc/filesystems
відобразить усі файлові системи, які наразі підтримуються запущеним ядром (як зазначено у другому стовпчику файлу /proc/filesystems
), тому масив FS
тепер містить один елемент для кожної файлової системи, що підтримується. Для ініціалізації масиву можна використовувати будь-які текстові дані, оскільки за замовчуванням будь-які слова, розділені символами space, tab або newline, стануть елементом масиву.
Tip
|
Bash розглядає кожен символ |
Арифметичні вирази
Bash надає практичний метод виконання цілочисельних арифметичних операцій за допомогою вбудованої команди expr
. Дві числові змінні, наприклад, $VAL1
і $VAL2
, можна додати разом за допомогою наступної команди:
$ SUM=`expr $VAL1 + $VAL2`
Отримане значення прикладу буде доступне у змінній $SUM
. Команду expr
можна замінити на $(())
, тож попередній приклад можна переписати як SUM=$(( $VAL1 + $VAL2 ))
. Вирази ступеня також дозволені з оператором подвійних зірочок, тому попереднє оголошення масиву SIZES=( 1048576 1073741824)
можна переписати як SIZES=( $((1024**2)) $((1024) **3)) )
.
Підстановку команд також можна використовувати в арифметичних виразах. Наприклад, файл /proc/meminfo
містить детальну інформацію про системну пам’ять, включаючи кількість вільних байтів в ОЗП:
$ FREE=$(( 1000 * `sed -nre '2s/[^[:digit:]]//gp' < /proc/meminfo` ))
У прикладі показано, як команду sed
можна використовувати для аналізу вмісту /proc/meminfo
всередині арифметичного виразу. Другий рядок файлу /proc/meminfo
містить обсяг вільної пам’яті в тисячах байтів, тому арифметичний вираз множить його на 1000, щоб отримати кількість вільних байтів в RAM.
Умовне виконання
Деякі сценарії зазвичай призначені не для виконання всіх команд у файлі сценарію, а лише для тих команд, які відповідають попередньо визначеним критеріям. Наприклад, сценарій обслуговування може надіслати попередження на електронну пошту адміністратора, лише якщо виконати команду не вдається. Bash надає спеціальні методи оцінки успішності виконання команд і загальні умовні структури, більш схожі на ті, які є в популярних мовах програмування.
Розділивши команди за допомогою &&
, команда праворуч буде виконана, лише якщо команда ліворуч не виявила помилки, тобто якщо її статус виходу дорівнював 0
:
COMMAND A && COMMAND B && COMMAND C
Протилежна поведінка відбувається, якщо команди розділені символом ||
. У цьому випадку наступна команда буде виконана лише в тому випадку, якщо попередня команда виявила помилку, тобто якщо її повернутий код статусу відрізняється від 0.
Однією з найважливіших особливостей усіх мов програмування є можливість виконання команд залежно від попередньо визначених умов. Найпростішим способом умовного виконання команд є використання вбудованої команди Bash if
, яка виконує одну або кілька команд, лише якщо команда, задана як аргумент, повертає код статусу 0 (успішно). Інша команда, test
, може бути використана для оцінки багатьох різних спеціальних критеріїв, тому вона здебільшого використовується в поєднанні з if
. У наведеному нижче прикладі повідомлення Confirmed: /bin/bash is executable.
буде показано, якщо файл /bin/bash
існує і він виконуваний:
if test -x /bin/bash ; then echo "Confirmed: /bin/bash is executable." fi
Опція -x
змушує команду test
повертати код статусу 0, лише якщо вказаний шлях є виконуваним файлом. У наступному прикладі показано інший спосіб досягнення точно такого ж результату, оскільки квадратні дужки можна використовувати як заміну для test
:
if [ -x /bin/bash ] ; then echo "Confirmed: /bin/bash is executable." fi
Інструкція else
є необов’язковою для структури if
і може, за наявності, визначати команду або послідовність команд для виконання, якщо умовний вираз не є істинним:
if [ -x /bin/bash ] ; then echo "Confirmed: /bin/bash is executable." else echo "No, /bin/bash is not executable." fi
Структури if
мають завжди закінчуватися на fi
, щоб інтерпретатор Bash знав, де закінчуються умовні команди.
Виведення сценарію
Навіть якщо мета сценарію включає лише операції, орієнтовані на файли, важливо відображати повідомлення, пов’язані з прогресом, у стандартному виведенні, щоб користувач був поінформований про будь-які проблеми та міг зрештою використовувати ці повідомлення для створення журналів операцій.
Вбудована команда Bash echo
зазвичай використовується для відображення простих рядків тексту, але вона також надає деякі розширені функції. З опцією -e
команда echo
може відображати спеціальні символи за допомогою екранованих послідовностей (послідовність зворотної похилої риски, що позначає спеціальний символ). Наприклад:
#!/bin/bash # Get the operating system's generic name OS=$(uname -o) # Get the amount of free memory in bytes FREE=$(( 1000 * `sed -nre '2s/[^[:digit:]]//gp' < /proc/meminfo` )) echo -e "Operating system:\t$OS" echo -e "Unallocated RAM:\t$(( $FREE / 1024**2 )) MB"
Хоча використання лапок є необов’язковим при використанні echo
без параметрів, необхідно додати їх при використанні параметра -e
, інакше спеціальні символи можуть відтворюватися неправильно. У попередньому сценарії обидві команди echo
використовують символ табуляції \t
для вирівнювання тексту, що призводить до такого результату:
Operating system: GNU/Linux Unallocated RAM: 1491 MB
Символ нового рядка \n
може бути використаний для розділення рядків виведення, тому точно таке виведення буде отримано шляхом поєднання двох команд echo
в одну:
echo -e "Operating system:\t$OS\nUnallocated RAM:\t$(( $FREE / 1024**2 )) MB"
Хоча команда echo
підходить для відображення більшості текстових повідомлень, вона може не підходити для відображення більш конкретних текстових шаблонів. Вбудована команда bash printf
дає більше контролю над тим, як відображати змінні. Команда printf
використовує перший аргумент як формат виведення, де заповнювачі будуть замінені наступними аргументами в тому порядку, в якому вони з’являються в командному рядку. Наприклад, повідомлення з попереднього прикладу можна створити за допомогою такої команди printf
:
printf "Operating system:\t%s\nUnallocated RAM:\t%d MB\n" $OS $(( $FREE / 1024**2 ))
Заповнювач %s
призначений для текстового вмісту (його буде замінено змінною $OS
), а %d
призначений для цілих чисел (його буде замінено результуючою кількістю вільних мегабайт в RAM ). printf
не додає символ нового рядка в кінці тексту, тому символ нового рядка \n
слід розмістити в кінці шаблону, якщо це необхідно. Весь шаблон слід інтерпретувати як один аргумент, тому він повинен бути взятий у лапки.
Tip
|
Формат заміни заповнювача, який виконується |
За допомогою printf
змінні розміщуються поза текстовим шаблоном, що дає змогу зберігати текстовий шаблон в окремій змінній:
MSG='Operating system:\t%s\nUnallocated RAM:\t%d MB\n' printf "$MSG" $OS $(( $FREE / 1024**2 ))
Цей метод особливо корисний для відображення різних вихідних форматів залежно від вимог користувача. Це полегшує, наприклад, написання сценарію, який використовує чіткий текстовий шаблон, якщо користувачеві потрібен список CSV (значення, розділені комами), а не стандартне вихідне повідомлення.
Вправи до посібника
-
Параметр
-s
командиread
корисний для введення паролів, оскільки він не відображатиме вміст, який вводиться на екрані. Як можна використати командуread
для збереження введених користувачем даних у зміннійPASSWORD
, приховуючи введений вміст? -
Єдиною метою команди
whoami
є відображення імені користувача, який її викликав, тому вона здебільшого використовується всередині сценаріїв для ідентифікації користувача, який їх виконує. Як можна зберегти в сценарії Bash результат командиwhoami
у змінній з назвоюWHO
? -
Який оператор Bash має бути між командами
apt-get dist-upgrade
іsystemctl reboot
, якщо користувач root хоче виконатиsystemctl reboot
, лише якщоapt-get dist-upgrade
завершено успішно?
Дослідницькі вправи
-
Після спроби запустити щойно створений сценарій Bash користувач отримує таке повідомлення про помилку:
bash: ./script.sh: Permission denied
Враховуючи, що файл
./script.sh
створено тим самим користувачем, яка ймовірна причина цієї помилки? -
Припустимо, що файл сценарію з назвою
do.sh
є виконуваним і символічне посилання з назвоюundo.sh
вказує на нього. Як за допомогою сценарію визначити, яке ім’я файлу виклику було --do.sh
чиundo.sh
? -
У системі з правильно налаштованою службою електронної пошти команда
mail -s "Maintenance Error" root <<<"Scheduled task error"
надсилає сповіщення електронною поштою користувачеві root. Таку команду можна використовувати в автоматичних завданнях, наприклад, cronjobs, щоб повідомити системного адміністратора про неочікувану проблему. Напишіть конструкцію if, яка виконає вищезгадану командуmail
, якщо статус завершення попередньої команди, яким би він не був, є невдалим.
Підсумки
Цей урок охоплює основні поняття для розуміння та написання сценаріїв оболонки Bash. Сценарії оболонки є основною частиною будь-якого дистрибутива Linux, оскільки вони пропонують дуже гнучкий спосіб автоматизації завдань користувача та системи, що виконуються в середовищі оболонки. В уроці розглянуто наступні питання:
-
Структура сценарію оболонки та правильні дозволи файлу сценарію
-
Параметри сценарію
-
Використання змінних для читання введених користувачем даних і збереження результатів команд
-
Масиви Bash
-
Прості тести та умовне виконання
-
Форматування виведення
Розглянуті команди та процедури:
-
Вбудована нотація Bash для підстановки команд, розширення масиву та арифметичних виразів
-
Умовне виконання команд за допомогою операторів
||
і&&
-
echo
-
chmod
-
exec
-
read
-
declare
-
test
-
if
-
printf
Відповіді до вправ посібника
-
Параметр
-s
командиread
корисний для введення паролів, оскільки він не відображатиме вміст, який вводиться на екрані. Як можна використати командуread
для збереження введених користувачем даних у зміннійPASSWORD
, приховуючи введений вміст?read -s PASSWORD
-
Єдиною метою команди
whoami
є відображення імені користувача, який її викликав, тому вона здебільшого використовується всередині сценаріїв для ідентифікації користувача, який їх виконує. Як можна зберегти в сценарії Bash результат командиwhoami
у змінній з назвоюWHO
?WHO=`whoami`
orWHO=$(whoami)
-
Який оператор Bash має бути між командами
apt-get dist-upgrade
іsystemctl reboot
, якщо користувач root хоче виконатиsystemctl reboot
, лише якщоapt-get dist-upgrade
завершено успішно?Оператор
&&
, як уapt-get dist-upgrade && systemctl reboot
.
Відповіді до дослідницьких вправ
-
Після спроби запустити щойно створений сценарій Bash користувач отримує таке повідомлення про помилку:
bash: ./script.sh: Permission denied
Враховуючи, що файл
./script.sh
створено тим самим користувачем, яка ймовірна причина цієї помилки?Для файлу
./script.sh
не ввімкнено дозвіл на виконання. -
Припустимо, що файл сценарію з назвою
do.sh
є виконуваним і символічне посилання з назвоюundo.sh
вказує на нього. Як за допомогою сценарію визначити, яке ім’я файлу виклику було --do.sh
чиundo.sh
?Спеціальна змінна
$0
містить назву файлу, який використовується для виклику сценарію. -
У системі з правильно налаштованою службою електронної пошти команда
mail -s "Maintenance Error" root <<<"Scheduled task error"
надсилає сповіщення електронною поштою користувачеві root. Таку команду можна використовувати в автоматичних завданнях, як-от cronjobs, щоб повідомити системного адміністратора про неочікувану проблему. Напишіть конструкцію if, яка виконає вищезгадану командуmail
, якщо статус завершення попередньої команди — яким би він не був — є невдалим.if [ "$?" -ne 0 ]; then mail -s "Maintenance Error" root <<<"Scheduled task error"; fi