У цій статті простими словами пояснюється, як у Bash працюють умови if, else, elif, а також цикли for, while і until. Без зайвої теорії та складних термінів – лише те, що справді знадобиться на практиці. Ви зрозумієте, як Bash визначає, виконувати команду чи ні, як проходити по списках, рахувати і повторювати дії потрібну кількість разів.
У цьому розділі серії Bash для початківців ви дізнаєтеся, як у Bash-скриптах використовуються if-else, вкладені умови та оператор case.
Ви дізнаєтеся, як використовувати умовні конструкції у Bash-скриптах, щоб вони поводилися по-різному в різних ситуаціях і випадках. Завдяки цьому ви зможете створювати набагато ефективніші Bash-скрипти, а також додавати перевірку помилок у свій код.
Найбазовішою і найважливішою конструкцією в будь-якій логіці прийняття рішень є умова if. Загальний синтаксис простого оператора if виглядає так:
if [ condition ]; then your code fi
Оператор if закривається словом fi. Це слово є зворотним до if.
Зверніть особливу увагу на пробіли.
Між відкривальною та закривальною дужками і умовою обов’язково має бути пробіл. Якщо його не буде, оболонка повідомить про помилку.
Також пробіли мають бути до і після оператора порівняння (
=,==,<=та інших). Інакше ви побачите помилку на кшталт “unary operator expected”.
Тепер створімо приклад скрипта root.sh. Цей скрипт виведе повідомлення «you are root» лише в тому випадку, якщо ви запустите його від імені користувача root:
#!/bin/bash
if [ $(whoami) = 'root' ]; then
echo "You are root"
fi
Команда whoami виводить ім’я поточного користувача. З уроку про змінні в Bash ви вже знаєте, що синтаксис $(команда) використовується для підстановки команд і дозволяє отримати їхній вивід.
Умова $(whoami) = 'root' буде істинною лише тоді, коли ви увійшли в систему під користувачем root. Не вірите? І не потрібно. Просто запустіть команду і переконайтеся в цьому самі.
Можливо, ви вже помітили, що під час запуску скрипта root.sh від імені звичайного користувача він нічого не виводить. Код, який має виконуватися тоді, коли умова if не виконується, можна додати в блок else. Це виглядає приблизно так:
#!/bin/bash
if [ $(whoami) = 'root' ]; then
echo "You are root"
else
echo "You are not root"
fi
Тепер, якщо запустити цей скрипт від імені звичайного користувача, він нагадає вам, що ви не всемогутній користувач root:
kabary@handbook:~$ ./root.sh You are not root
Оператор elif (else if) використовується тоді, коли потрібно перевірити одразу кілька умов.
Наприклад, наступний Bash-скрипт age.sh приймає ваш вік як аргумент і виводить відповідне повідомлення залежно від цього віку:
#!/bin/bash
AGE=$1
if [ $AGE -lt 13 ]; then
echo "You are a kid."
elif [ $AGE -lt 20 ]; then
echo "You are a teenager."
elif [ $AGE -lt 65 ]; then
echo "You are an adult."
else
echo "You are an elder."
fi
На цьому етапі ви вже знаєте, як передавати аргументи Bash-скриптам. Тож давайте кілька разів запустимо скрипт age.sh і перевіримо, як він працює з різними значеннями віку:
kabary@handbook:~$ ./age.sh 11 You are a kid. kabary@handbook:~$ ./age.sh 18 You are a teenager. kabary@handbook:~$ ./age.sh 44 You are an adult. kabary@handbook:~$ ./age.sh 70 You are an elder.
Зверніть увагу, що тут використовується умова перевірки -lt (менше ніж) разом зі змінною $AGE.
Також пам’ятайте, що в одній конструкції if можна використовувати кілька операторів elif, але лише один else. Увесь блок if обов’язково має закриватися словом fi.
У Bash також можна використовувати оператор if всередині іншого if. Наприклад, подивіться на наступний Bash-скрипт weather.sh:
#!/bin/bash
TEMP=$1
if [ $TEMP -gt 5 ]; then
if [ $TEMP -lt 15 ]; then
echo "The weather is cold."
elif [ $TEMP -lt 25 ]; then
echo "The weather is nice."
else
echo "The weather is hot."
fi
else
echo "It's freezing outside ..."
fi
Цей скрипт приймає будь-яке значення температури як аргумент і виводить повідомлення, яке показує, якою буде погода. Якщо температура перевищує п’ять градусів, тоді перевіряється вкладена умова if-elif.
Давайте запустимо скрипт кілька разів і подивимося, як він працює на практиці:
kabary@handbook:~$ ./weather.sh 0 It's freezing outside ... kabary@handbook:~$ ./weather.sh 8 The weather is cold. kabary@handbook:~$ ./weather.sh 16 The weather is nice. kabary@handbook:~$ ./weather.sh 30 The weather is hot.
У Bash також можна використовувати оператор case як заміну кільком умовам if, оскільки такі конструкції іноді бувають заплутаними й важкими для читання. Загальний синтаксис оператора case виглядає так:
case "variable" in
"pattern1" )
Command … ;;
"pattern2" )
Command … ;;
"pattern2" )
Command … ;;
esac
Зверніть увагу!
Шаблони завжди записуються з пробілом перед символом
).
Команди в блоці case завжди завершуються подвійною крапкою з комою
;;. Пробіл перед ними не є обов’язковим.
Оператор case закривається словом esac. Це слово є зворотним до case.
Оператор case особливо зручний під час роботи з шаблонами або регулярними виразами. Щоб це показати, подивіться на наступний Bash-скрипт char.sh:
#!/bin/bash CHAR=$1 case $CHAR in [a-z]) echo "Small Alphabet." ;; [A-Z]) echo "Big Alphabet." ;; [0-9]) echo "Number." ;; *) echo "Special Character." esac
Цей скрипт приймає один символ як аргумент і визначає, чим він є: малою або великою літерою, числом чи спеціальним символом.
kabary@handbook:~$ ./char.sh a Small Alphabet. kabary@handbook:~$ ./char.sh Z Big Alphabet. kabary@handbook:~$ ./char.sh 7 Number. kabary@handbook:~$ ./char.sh $ Special Character.
Зверніть увагу, що тут використовується символ підстановки зірочка * для задання стандартного випадку. Він є аналогом блоку else в умові if.
В Bash існує багато умов перевірки, які можна використовувати разом з оператором if. Вибір умови залежить від того, з чим ви працюєте: числами, рядками чи файлами. По суті, їх можна сприймати як логічні оператори в Bash.
Нижче наведено деякі з найпоширеніших умов перевірки:
На щастя, вам не потрібно запам’ятовувати всі ці умови перевірки, адже їх завжди можна подивитися в довідці man test:
kabary@handbook:~$ man test
Створімо фінальний скрипт з назвою filetype.sh, який визначає, чи є файл звичайним файлом, директорією або символічним посиланням:
#!/bin/bash
if [ $# -ne 1 ]; then
echo "Error: Invalid number of arguments"
exit 1
fi
file=$1
if [ -f $file ]; then
echo "$file is a regular file."
elif [ -L $file ]; then
echo "$file is a soft link."
elif [ -d $file ]; then
echo "$file is a directory."
else
echo "$file does not exist"
fi
У скрипті додано просту перевірку кількості аргументів. Якщо аргумент не передано або їх більше одного, виконання одразу зупиняється і виводиться повідомлення, без запуску решти коду.
Тепер можна кілька разів запустити скрипт і подивитися, як він поводиться з різними типами файлів:
kabary@handbook:~$ ./filetype.sh weather.sh weather.sh is a regular file. kabary@handbook:~$ ./filetype.sh /bin /bin is a soft link. kabary@handbook:~$ ./filetype.sh /var /var is a directory. kabary@handbook:~$ ./filetype.sh Error: Invalid number of arguments
Раніше всі конструкції if else використовувалися в окремих Bash-скриптах. Це зручно і правильно, але не завжди обов’язково.
Коли потрібно просто швидко перевірити умову і подивитися на результат, if else можна записати в один рядок і виконати прямо в терміналі, без створення окремого файлу.
Припустімо, є такий Bash-скрипт:
if [ $(whoami) = 'root' ]; then
echo "You are root"
else
echo "You are not root"
fi
Усі конструкції if else можна записати в одному рядку ось таким чином:
if [ $(whoami) = 'root' ]; then echo "root"; else echo "not root"; fi
Цей код можна просто скопіювати в термінал і одразу подивитися, що він робить. Логіка проста: після кожної команди ставляться крапки з комою, а далі йде наступна умова if else.
У підсумку стає набагато зрозуміліше, як працюють умовні конструкції в Bash і як за їх допомогою робити скрипти більш гнучкими. А щоб закріпити все на практиці, можна виконати кілька простих вправ із Bash у PDF нижче. Там же є й готові відповіді.
Цикли є важливою частиною будь-якої мови сценаріїв. У цьому розділі серії Bash для початківців розглядаються цикли for, while та until на зрозумілих прикладах.
Можливість використовувати цикли є дуже потужною функцією Bash-скриптів. Цикли мають багато різних сценаріїв застосування і значно спрощують автоматизацію задач.
У цьому уроці розглядаються три основні типи циклів у Bash. Також показано, як використовувати цикли для проходу по елементах масивів. Крім того, пояснюється, як застосовувати оператори break і continue для керування роботою циклів, а наприкінці розглядається створення нескінченних циклів.
Цикл for є одним із трьох типів циклічних конструкцій, доступних у Bash. Існує два основні способи запису циклу for.
C-подібний синтаксис циклу for
Використання циклу for для списку або діапазону значень
Тим, хто знайомий з мовами програмування на кшталт C або C++, буде легко впізнати такий синтаксис циклу for:
for ((initialize ; condition ; increment)); do [COMMANDS] done
Використовуючи згаданий C-подібний синтаксис, наведений нижче цикл for виведе повідомлення «Hello Friend» десять разів:
for ((i = 0 ; i < 10 ; i++)); do
echo "Hello Friend"
done
Спочатку цикл for ініціалізує цілочисельну змінну i значенням нуль, після чого перевіряє умову (i < 10). Якщо умова виконується, цикл виводить рядок echo “Hello Friend”, збільшує значення змінної i на 1 і запускається знову. Цей процес повторюється доти, доки i не стане більшим або рівним 10.
kabary@handbook:~$ bash hello.sh Hello Friend Hello Friend Hello Friend Hello Friend Hello Friend Hello Friend Hello Friend Hello Friend Hello Friend Hello Friend
Існує ще один варіант синтаксису циклу for, який особливо зручний під час роботи зі списком файлів або рядків, діапазоном чисел, масивами чи виводом команд. Цикл for зі списком або діапазоном має такий вигляд:
for item in [LIST]; do [COMMANDS] done
Наприклад, наступний цикл for виконує точно те саме, що й C-подібний цикл for, розглянутий у попередньому розділі:
for i in {1..10}; do
echo "Hello Friend"
done
Наведений нижче скрипт var.sh виведе всі файли та директорії, які знаходяться в каталозі /var:
#!/bin/bash
for i in /var/*; do
echo $i
done
Нижче наведено приклад виводу під час запуску скрипта var.sh:
kabary@handbook:~$ ./var.sh /var/backups /var/cache /var/crash /var/lib /var/local /var/lock /var/log /var/mail /var/metrics /var/opt /var/run /var/snap /var/spool /var/tmp
Цикл while є ще одним популярним і зрозумілим типом циклу, який можна використовувати в Bash-скриптах. Загальний синтаксис циклу while у Bash виглядає так:
while [ condition ]; do [COMMANDS] done
Наприклад, наведений нижче скрипт 3×10.sh використовує цикл while і виводить перші десять кратних числа три:
#!/bin/bash
num=1
while [ $num -le 10 ]; do
echo $(($num * 3))
num=$(($num+1))
done
Ось вивід наведеного вище скрипта:
kabary@handbook:~$ ./3x10.sh 3 6 9 12 15 18 21 24 27 30
Спочатку змінна num ініціалізується значенням 1. Далі цикл while виконується доти, доки num є меншим або дорівнює 10. Усередині тіла циклу команда echo виводить значення num, помножене на три, після чого значення num збільшується на 1.
Тим, хто має досвід роботи з C або C++, може бракувати циклу do-while, але в Bash такого циклу не існує. Натомість у Bash є інший тип циклу – until. Цикл until використовує той самий синтаксис, що й while:
until [ condition ]; do [COMMANDS] Done
Різниця між while і until проста і зводиться до умови. Цикл while виконується, поки умова виконується. Цикл until, навпаки, працює доти, доки умова не стане істинною.
Той самий скрипт 3×10.sh без проблем можна написати через until замість while. Потрібно лише змінити логіку перевірки умови на протилежну:
#!/bin/bash
num=1
until [ $num -gt 10 ]; do
echo $(($num * 3))
num=$(($num+1))
done
Зверніть увагу, що заперечення умови перевірки [ $num -le 10 ] виглядає як [ $num -gt 10 ].
Тепер, коли цикли в Bash-скриптах уже знайомі, можна рухатися далі.
Тим, хто проходить цю серію уроків із самого початку, масиви в Bash уже мають бути знайомі. Цикл for найчастіше використовується для перебору елементів масиву.
Наприклад, наступний скрипт prime.sh проходить по масиву prime і виводить кожен його елемент:
#!/bin/bash
prime=(2 3 5 7 11 13 17 19 23 29)
for i in "${prime[@]}"; do
echo $i
done
Ось вивід скрипта prime.sh:
kabary@handbook:~$ ./prime.sh 2 3 5 7 11 13 17 19 23 29
Бувають ситуації, коли цикл не має сенсу виконувати до кінця. Інколи достатньо зупинити його раніше або пропустити один крок. Для цього в Bash використовуються break і continue. break просто обриває цикл і виконання йде далі, після нього.
У прикладі нижче цикл зупиняється раніше і виводить лише числа від одного до трьох:
for ((i=1;i<=10;i++)); do
echo $i
if [ $i -eq 3 ]; then
break
fi
done
Оператор continue використовується тоді, коли потрібно пропустити один крок циклу. Цикл не зупиняється, а просто одразу переходить до наступної ітерації, не виконуючи решту команд на поточному кроці.
У прикладі нижче скрипт odd.sh проходиться по числах від одного до десяти і виводить тільки непарні, ігноруючи всі парні значення:
#!/bin/bash
for ((i=0;i<=10;i++)); do
if [ $(($i % 2)) -ne 1 ]; then
continue
fi
echo $i
done
Ось вивід, у якому відображаються непарні числа:
kabary@handbook:~$ ./odd.sh 1 3 5 7 9
Нескінченний цикл – це ситуація, коли цикл не зупиняється і продовжує виконуватися знову і знову. Так відбувається тоді, коли умова виходу з циклу ніколи не спрацьовує.
Найчастіше такі цикли з’являються через просту логічну помилку. Наприклад, під час спроби вивести числа від 1 до 10 у зворотному порядку легко ненавмисно написати цикл, який ніколи не завершиться:
for ((i=10;i>0;i++)); do
echo $i
done
Проблема полягає в тому, що цикл постійно збільшує значення змінної i на 1. Щоб це виправити, потрібно замінити i++ на i–, ось так:
for ((i=10;i>0;i--)); do
echo $i
done
У деяких випадках нескінченні цикли створюються навмисно, наприклад, щоб чекати на виконання певної зовнішньої умови в системі. Створити нескінченний цикл for у Bash можна дуже просто, ось так:
for ((;;)); do
[COMMANDS]
done
Якщо потрібно створити нескінченний цикл while, це можна зробити таким чином:
while [ true ]; do
[COMMANDS]
done
Цей розділ допомагає краще зрозуміти, як працюють цикли в Bash і як ефективно використовувати їх у скриптах.