У Bash є дві форми циклів:
for
Щоб перебрати список елементів, використовуємо цикл for.
for varname in words; do COMMANDS; done
words розгортається за допомогою розбиття на слова та розгортання імен файлів (про які ми дізналися в концепції лапок).
Кожне слово по черзі присвоюється змінній varname, і виконуються COMMANDS.
Ось кілька прикладів, які показують, коли цикл for може стати в пригоді
Виконати якісь команди над групою файлів.
# source all the bash library files in the current directory
for file in ./*.bash; do
source "$file"
done
Виконати якусь команду для кожного з параметрів скрипта або функції.
for arg in "$@"; do
echo "argument: ${arg}"
done
Виконати якусь команду для кожного слова, відокремленого пробільними символами, у вихідних даних команди.
for i in $(seq 10); do
echo "${i} squared is $((i * i))"
done
Далі ми побачимо кілька застережень щодо цього стилю.
for
У Bash справді є арифметичний цикл. Дещо знайомим він буде тим, хто програмував іншими мовами.
for ((INITIALIZATION; CONDITION; INCREMENT)); do COMMANDS; done
Подвійні дужки в Bash позначають арифметичний контекст (докладніше про арифметику в Bash ми дізнаємося в одній із наступних концепцій).
Наведений вище приклад із seq можна записати так
for ((i = 1; i <= 10; i++)); do
echo "${i} squared is $((i * i))"
done
for
Не читаймо рядки за допомогою for.
Це антипатерн, який часто можна побачити: перебирання вихідних даних cat.
for line in $(cat some.file); do
do_something_with "$line"
done
Тут помилка з двох причин.
for перебирає слова у файлі, а не рядки.Як ми дізналися з концепції лапок, розбиттям на слова можна керувати за допомогою змінної IFS, а розгортання імен файлів можна вимкнути.
Але таке рішення зазвичай виявляється крихким.
Для перебирання рядків файлу for не годиться.
Ідіоматичний спосіб читати файл використовує цикл while.
while
Використовуємо while, щоб повторювати послідовність команд, поки деяка умова є правдою.
while CONDITION_COMMANDS; do COMMANDS; done
Як і з if (про це ми дізналися з концепції умовних конструкцій), для CONDITION_COMMANDS немає спеціального синтаксису.
Статус виходу списку команд визначатиме «правду» чи «неправду».
Команда break перериває цикл.
Керування повертається до команди, що йде після завершувача done циклу.
Команда continue переходить до наступної ітерації циклу.
Як уже згадувалося, ідіоматичний спосіб читати файл використовує цикл while.
while IFS= read -r line; do
do_something_with "$line"
done < some.file
Вміст файлу подається на вхід циклу за допомогою перенаправлення <.
Команда read повертає «правду», якщо може прочитати повний рядок зі своїх вхідних даних.
Коли вхідні дані вичерпано, або якщо останній рядок закінчується без завершального символу нового рядка, read повертає «неправду».
По-справжньому ідіоматичний спосіб читати рядки файлу, навіть якщо останній рядок не закінчується символом нового рядка, такий:
while IFS= read -r line || [[ -n "$line" ]]; do ...
Опція -r вказує read не замінювати послідовності зі зворотною скосою рискою: зворотна скоса риска є просто звичайним символом.
IFS= присвоює порожній рядок тексту змінній IFS лише на час виконання команди read.
Це тимчасово вимикає розбиття на слова, щоб будь-які пробільні символи на початку чи в кінці вхідного рядка тексту зберігалися.
Конструкція until повторює послідовність команд, поки деяка умова є неправдою.
Цикл повторюється, поки умова не стане правдою.
until CONDITION_COMMANDS; do COMMANDS; done
until використовують рідко.
Частіше використовують цикл «while not».
while ! CONDITION_COMANDS; do ...
Іноді нам потрібно, щоб цикл виконувався вічно. Ось два способи це зробити.
цикл while з умовою, яка завжди має успішний статус виходу
while true; do ...
арифметичний цикл for з порожніми виразами
for ((;;)); do ...
Явної конструкції do-while немає, але того самого ефекту можна досягти так:
while true; do
COMMANDS
if END_CONDITION; then
break
fi
done