Циклы в bash

for

С-like конструкция for

Конструкция, объединяющая код инициализации, условие завершения и код, выполняемый в начале каждой итерации:

for ((i=0; i<10; i++)); do
	echo "$i"
done

Явные значения

Передать в for можно явный список значений из чисел:

for num in 1 2 3 4 5; do
	echo "$num"
done

из строк:

for person in Doka Hasan Gadji Umar; do
	echo "$person"
done

из переменных:

for person in $ME $3 Umar ${RA[2]} Doka; do
	echo "$person"
done

из результата выполнения команд - отдельных или их конвейеров:

for arg in $(some cmd or other | sort -u)
for arg in $(cat some_file)
for arg in $(< some_file)  # быстрее, чем обращаться в cat
for pic in $(find . -name '*.jpg*')
for val in $(find . -type d | LC_ALL=C sort)

из результата команды seq:

for i in $(seq 1 10)

Но вместо такой конструкции лучше использовать более эффективный и понятный способ - С-like конструкцию:

for ((i=1; i<=10; i++))

seq вообще удобно использовать для последовательности float чисел, указав начальное значение, приращение на каждом шаге и конечное значение:

for value in $(seq 2.1 0.3 3.2); do
	echo "$value"
done
# вывод:
2,1
2,4
2,7
3,0

Существует вариант с фигурными скобками, но с проблемами совместимости с разными версиями bash, потому что поддержка фигурных скобок появилась в bash 3.0, а дополнение нулями числовых значений (в начале) в bash 4.0:

for i in {01..10}
# более используемый вариант:
for filename in log{01..5}.txt

Нули в начале числа

В bash 4.0 и выше, если любой из первых двух членов в выражении вида {начало..конец..шаг} начинается с нуля, то переменная цикла будет значением одинаковой длины и дополняться нулями слева.
Примеры:
{098..100} 098,099,100
{98..0100} 0098,0099,0100

Можно и передать несколько подстановок и вообще итераторов:

for VAL in $(ls | grep test) {0..3}; do
	echo $VAL
done
 
# test14
# test15
# test16
# 0
# 1
# 2
# 3

Бесконечные цикл for

for ((;;)); do
	echo "4evuh"
done

Перечисление значений в списке

Это, кстати, python-like конструкция:

for person in "${person_list[@]}"; do
	echo "$person"
done
# Например, такой вывод:
Doka
Umar
John
Lora

"${person_list[@]}" - конструкция перечисления всех значений в списке. Подстановка производится при подготовке команды к выполнению. Содержимое массива извлекается до передачи управления оператору for, то есть, цикл получается значения, как если бы они были введены явно:

for person in Doka Umar John Lora

Тоже самое можно делать с ассоциативными массивами/хешами (аналогия - словари в python):

 # Объявление хеша (массив пар ключ/значение)
declare -A hash
 
# Запись данных в хеш
while read key value; do
	hash[$key]="$value"
done
 
# Show hash content (может выводиться не в порядке записи)
for key in "${!hash[@]}"; do
	echo "key ${key}: value ${hash[$key]}"
done

Еще пример:

 # Объявление хеша (массив пар ключ/значение)
declare -A hash
 
# Запись данных в хеш: слова и кол-во вхождений
while read key value; do
	let hash[$word]+="$count"
done
 
# Show hash content (может выводиться не в порядке записи)
for key in "${!hash[@]}"; do
	echo "word ${key} count: ${hash[$key]}"
done
Кавычки

Значения в массивах могут иметь пробелы, поэтому использование кавычек в случае с "${person_list[@]}" позволит избежать неожиданных результатов:

for person in ${person_list[@]}; do
	echo "$person"
done
 
# Перед выполнением команды конструкция ${person_list[@]} будет преобразована в:
# Doka Umarov John Doe SomeName SomeSurname
 
# Вывод (неправильный):
Doka
Umarov
John
Doe
SomeName
SomeSurname

А кавычки помогут избежать этого:

for person in "${person_list[@]}"; do
	echo "$person"
done
 
# Перед выполнением команды конструкция ${person_list[@]} будет преобразована в:
# "Doka Umarov" "John Doe" "SomeName SomeSurname"
 
# Вывод (неправильный):
Doka Umarov
John Doe
SomeName SomeSurname

Еще один момент: к синтаксисе списка можно использовать знаки @ и * для перечисления всех его элементов. Конструкция ${someList[*]} дает тот же результат за исключением случая, когда она заключена в кавычки: "${someList[*]}" вернет все значения внутри одной строки:

for person in "person_list[*]"; do
	echo "$person"
done
 
# Вывод
Doka Umarov John Doe SomeName SomeSurname

Перебор аргументов сценария

Для перебора всех аргументов сценария можно использовать специальные переменные командной оболочки $@ и $* , которые вызывают список всех аргументов сценария, но при заключении в кавычки дают разный результат: несколько строк и в одну строку.

Есть два хороших варианта перебора циклом for всех аргументов сценария:

for param
# и
for param in "$@"

В любом случае, комментарии лишними не будут (для тех, кто не шарит за bash).

while и until

C while всё просто:

while <условие>; do <тело>; done

until это ! while. Выполняет тело цикла, пока код условия выхода ненулевой.
Удобно в случае ожидания какого-то события, например, перезагрузки узла:

# Ожидание перезагрузки
until ssh user@10.10.10.10; do sleep 3; done
 
# Эквивалентен этому:
! while ssh user@10.10.10.10; do sleep 3; done

Соусы:
Книга “Идиомы Bash Глава 2. Язык циклов
Книга “Bash и кибербезопасность Глава 2. “Основы работы с bash

bash