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