Цикл — это блок команд, который исполняется многократно до тех пор, пока не будет выполнено условие выхода из цикла.
- for (in)
-
Это одна из основных разновидностей циклов. И она значительно отличается от аналога в языке C.
for arg in [list]
do
команда(ы)…
doneНа каждом проходе цикла, переменная-аргумент цикла arg последовательно, одно за другим, принимает значения из списка list.
-
for arg in «$var1« «$var2« «$var3« … «$varN«
-
# На первом проходе, $arg = $var1
-
# На втором проходе, $arg = $var2
-
# На третьем проходе, $arg = $var3
-
# …
-
# На N-ном проходе, $arg = $varN
-
# Элементы списка заключены в кавычки для того, чтобы предотвратить возможное разбиение их на отдельные аргументы (слова).
Элементы списка могут включать в себя шаблонные символы.
Есл ключевое слово do находится в одной строке со словом for, то после списка аргументов (перед do) необходимо ставить точку с запятой.
for arg in [list] ; do
Пример 10-1. Простой цикл for
-
#!/bin/bash
-
# Список планет.
-
for planet in Меркурий Венера Земля Марс Юпитер Сатурн Уран Нептун Плутон
-
do
-
echo $planet
-
done
-
echo
-
# Если ‘список аргументов’ заключить в кавычки, то он будет восприниматься как единственный аргумент .
-
for planet in «Меркурий Венера Земля Марс Юпитер Сатурн Уран Нептун Плутон»
-
do
-
echo $planet
-
done
-
exit 0
Каждый из элементов [списка] может содержать несколько аргументов. Это бывает полезным при обработке групп параметров. В этом случае, для принудительного разбора каждого из аргументов в списке, необходимо использовать инструкцию set (см. Пример 11-14).
Пример 10-2. Цикл for с двумя параметрами в каждом из элементов списка
-
#!/bin/bash
-
# Список планет.
-
# Имя кажой планеты ассоциировано с расстоянием от планеты до Солнца (млн. миль).
-
for planet in «Меркурий 36» «Венера 67» «Земля 93» «Марс 142» «Юпитер 483»
-
do
-
set — $planet # Разбиение переменной «planet» на множество аргументов (позиционных параметров).
-
# Конструкция «—» предохраняет от неожиданностей, если $planet «пуста» или начинается с символа «-«.
-
# Если каждый из аргументов потребуется сохранить, поскольку на следующем проходе они будут «забиты» новыми значениями,
-
# То можно поместить их в массив,
-
# original_params=(«$@»)
-
echo «$1 в $2,000,000 миль от Солнца»
-
#—-две табуляции—к параметру $2 добавлены нули
-
done
-
# (Спасибо S.C., за разъяснения.)
-
exit 0
В качестве списка, в цикле for, можно использовать переменную.
Пример 10-3. Fileinfo: обработка списка файлов, находящегося в переменной
-
#!/bin/bash
-
# fileinfo.sh
-
FILES=«/usr/sbin/privatepw
-
/usr/sbin/pwck
-
/usr/sbin/go500gw
-
/usr/bin/fakefile
-
/sbin/mkreiserfs
-
/sbin/ypbind» # Список интересующих нас файлов.
-
# В список добавлен фиктивный файл /usr/bin/fakefile.
-
echo
-
for file in $FILES
-
do
-
if [ ! -e «$file« ] # Проверка наличия файла.
-
then
-
echo «Файл $file не найден.»; echo
-
continue # Переход к следующей итерации.
-
fi
-
ls -l $file | awk ‘{ print $8 » размер: » $5 }’ # Печать 2 полей.
-
whatis `basename $file` # Информация о файле.
-
echo
-
done
-
exit 0
В [списке] цикла for могут быть использованы имена файлов, которые в свою очередь могут содержать символы-шаблоны.
Пример 10-4. Обработка списка файлов в цикле for
-
#!/bin/bash
-
# list-glob.sh: Создание список файлов в цикле for с использованием
-
# операции подстановки имен файлов («globbing»).
-
echo
-
for file in *
-
do
-
ls -l «$file« # Список всех файлов в $PWD (текущем каталоге).
-
# Напоминаю, что символу «*» соответствует любое имя файла,
-
# однако, в операциях подстановки имен файлов («globbing»),
-
# имеются исключения — имена файлов, начинающиеся с точки.
-
# Если в каталоге нет ни одного файла, соответствующего шаблону,
-
# то за имя файла принимается сам шаблон.
-
# Чтобы избежать этого, используйте ключ nullglob
-
# (shopt -s nullglob).
-
# Спасибо S.C.
-
done
-
echo; echo
-
for file in [jx]*
-
do
-
rm -f $file # Удаление файлов, начинающихся с «j» или «x» в $PWD.
-
echo «Удален файл \»$file\»«.
-
done
-
echo
-
exit 0
Если [список] в цикле for не задан, то в качестве оного используется переменная $@ — список аргументов командной строки. Оень остроумно эта особенность проиллюстрирована в Пример A-18.
Пример 10-5. Цикл for без списка аргументов
-
#!/bin/bash
-
# Попробуйте вызвать этот сценарий с аргументами и без них и посмотреть на результаты.
-
for a
-
do
-
echo -n «$a «
-
done
-
# Список аргументов не задан, поэтому цикл работает с переменной ‘$@’
-
#+ (список аргументов командной строки, включая пробельные символы).
-
echo
-
exit 0
При создании списка аргументов, в цикле for допускается пользоваться подстановкой команд. См. Пример 12-42, Пример 10-10 и Пример 12-36.
Пример 10-6. Создание списка аргументов в цикле for с помощью операции подстановки команд
-
#!/bin/bash
-
# Цикл for со [списком], созданным с помощью подстановки команд.
-
NUMBERS=«9 7 3 8 37.53»
-
for number in `echo $NUMBERS` # for number in 9 7 3 8 37.53
-
do
-
echo -n «$number «
-
done
-
echo
-
exit 0
Более сложный пример использования подстановки команд при создании списка аргументов цикла.
Пример 10-7. grep для бинарных файлов
-
#!/bin/bash
-
# bin-grep.sh: Поиск строк в двоичных файлах.
-
# замена «grep» для бинарных файлов.
-
# Аналогично команде «grep -a»
-
E_BADARGS=65
-
E_NOFILE=66
-
if [ $# -ne 2 ]
-
then
-
echo «Порядок использования: `basename $0` string filename»
-
exit $E_BADARGS
-
fi
-
if [ ! -f «$2» ]
-
then
-
echo «Файл \»$2\» не найден.»
-
exit $E_NOFILE
-
fi
-
for word in $( strings «$2» | grep «$1» )
-
# Инструкция «strings» возвращает список строк в двоичных файлах.
-
# Который затем передается по конвейеру команде «grep», для выполнения поиска.
-
do
-
echo $word
-
done
-
# Как указывает S.C., вышепрведенное объявление цикла for может быть упрощено
-
# strings «$2» | grep «$1» | tr -s «$IFS» ‘[\n*]’
-
# Попробуйте что нибудь подобное: «./bin-grep.sh mem /bin/ls»
-
exit 0
Еще один пример.
Пример 10-8. Список всех пользователей системы
-
#!/bin/bash
-
# userlist.sh
-
PASSWORD_FILE=/etc/passwd
-
n=1 # Число пользователей
-
for name in $(awk ‘BEGIN{FS=»:»}{print $1}’ < «$PASSWORD_FILE« )
-
# Разделитель полей = : ^^^^^^
-
# Вывод первого поля ^^^^^^^^
-
# Данные берутся из файла паролей ^^^^^^^^^^^^^^^^^
-
do
-
echo «Пользователь #$n = $name«
-
let «n += 1»
-
done
-
# Пользователь #1 = root
-
# Пользователь #2 = bin
-
# Пользователь #3 = daemon
-
# …
-
# Пользователь #30 = bozo
-
exit 0
И заключительный пример использования подстановки команд при создании [списка].
Пример 10-9. Проверка авторства всех бинарных файлов в текущем каталоге
-
#!/bin/bash
-
# findstring.sh:
-
# Поиск заданной строки в двоичном файле.
-
directory=/usr/local/bin/
-
fstring=«Free Software Foundation» # Поиск файлов от FSF.
-
for file in $( find $directory -type f -name ‘*’ | sort )
-
do
-
strings -f $file | grep «$fstring« | sed -e «s%$directory%%»
-
# Команде «sed» передается выражение (ключ -e),
-
#+ для того, чтобы изменить обычный разделитель «/» строки поиска и строки замены
-
#+ поскольку «/» — один из отфильтровываемых символов.
-
# Использование такого символа порождает сообщение об ошибке (попробуйте).
-
done
-
exit 0
-
# Упражнение:
-
# —————
-
# Измените сценарий таким образом, чтобы он брал
-
#+ $directory и $fstring из командной строки.
Результат работы цикла for может передаваться другим командам по конвейеру.
Пример 10-10. Список символических ссылок в каталоге
-
#!/bin/bash
-
# symlinks.sh: Список символических ссылок в каталоге.
-
directory=${1-`pwd`}
-
# По-умолчанию в текущем каталоге,
-
# Блок кода, который выполняет аналогичные действия.
-
# ———————————————————-
-
# ARGS=1 # Ожидается один аргумент командной строки.
-
#
-
# if [ $# -ne «$ARGS» ] # Если каталог поиска не задан…
-
# then
-
# directory=`pwd` # текущий каталог
-
# else
-
# directory=$1
-
# fi
-
# ———————————————————-
-
echo «символические ссылки в каталоге \»$directory\»«
-
for file in «$( find $directory -type l )« # -type l = символические ссылки
-
do
-
echo «$file«
-
done | sort # В противном случае получится неотсортированный список.
-
# Как отмечает Dominik ‘Aeneas’ Schnitzer,
-
#+ в случае отсутствия кавычек для $( find $directory -type l )
-
#+ сценарий «подавится» именами файлов, содержащими пробелы.
-
exit 0
Вывод цикла может быть перенаправлен со stdout в файл, ниже приводится немного модифицированный вариант предыдущего примера, демонстрирующий эту возможность.
Пример 10-11. Список символических ссылок в каталоге, сохраняемый в файле
-
#!/bin/bash
-
# symlinks.sh: Список символических ссылок в каталоге.
-
OUTFILE=symlinks.list # файл со списком
-
directory=${1-`pwd`}
-
# По-умолчанию — текущий каталог,
-
echo «символические ссылки в каталоге \»$directory\»« > «$OUTFILE«
-
echo «—————————« >> «$OUTFILE«
-
for file in «$( find $directory -type l )« # -type l = символические ссылки
-
do
-
echo «$file«
-
done | sort >> «$OUTFILE« # перенаправление вывода
-
# ^^^^^^^^^^^^^ в файл.
-
exit 0
Оператор цикла for имеет и альтернативный синтаксис записи — очень похожий на синтаксис оператора for в языке C. Для этого используются двойные круглые скобки.
Пример 10-12. C-подобный синтаксис оператора цикла for
-
#!/bin/bash
-
# Два вапианта оформления цикла.
-
echo
-
# Стандартный синтаксис.
-
for a in 1 2 3 4 5 6 7 8 9 10
-
do
-
echo -n «$a «
-
done
-
echo; echo
-
# +==========================================+
-
# А теперь C-подобный синтаксис.
-
LIMIT=10
-
for ((a=1; a <= LIMIT ; a++)) # Двойные круглые скобки и «LIMIT» без «$».
-
do
-
echo -n «$a «
-
done # Конструкция заимствована из ‘ksh93’.
-
echo; echo
-
# +=========================================================================+
-
# Попробуем и C-шный оператор «запятая».
-
for ((a=1, b=1; a <= LIMIT ; a++, b++)) # Запятая разделяет две операции, которые выполняются совместно.
-
do
-
echo -n «$a—$b «
-
done
-
echo; echo
-
exit 0
См. так же Пример 25-15, Пример 25-16 и Пример A-7.
—
А сейчас пример сценария, который может найти «реальное» применение.
Пример 10-13. Работа с командой efax в пакетном режиме
-
#!/bin/bash
-
EXPECTED_ARGS=2
-
E_BADARGS=65
-
if [ $# -ne $EXPECTED_ARGS ]
-
# Проверка наличия аргументов командной строки.
-
then
-
echo «Порядок использования: `basename $0` phone# text-file»
-
exit $E_BADARGS
-
fi
-
if [ ! -f «$2» ]
-
then
-
echo «Файл $2 не является текстовым файлом»
-
exit $E_BADARGS
-
fi
-
fax make $2 # Создать fax-файлы из текстовых файлов.
-
for file in $(ls $2.0*) # Все файлы, получившиеся в результате преобразования.
-
# Используется шаблонный символ в списке.
-
do
-
fil=«$fil $file«
-
done
-
efax -d /dev/ttyS3 -o1 -t «T$1» $fil # отправить.
-
# Как указывает S.C., в цикл for может быть вставлена сама команда отправки в виде:
-
# efax -d /dev/ttyS3 -o1 -t «T$1» $2.0*
-
# но это не так поучительно [;-)].
-
exit 0
-
- while
-
Оператор while проверяет условие перед началом каждой итерации и если условие истинно (если код возврата равен 0), то управление передается в тело цикла. В отличие от циклов for, циклы while используются в тех случаях, когда количество итераций заранее не известно.
while [condition]
do
command…
doneКак и в случае с циклами for/in, при размещении ключевого слова do в одной строке с объявлением цикла, необходимо вставлять символ «;» перед do.
while [condition] ; do
Обратите внимание: в отдельных случаях, таких как использование конструкции getopts совместно с оператором while, синтаксис несколько отличается от приводимого здесь.
Пример 10-14. Простой цикл while
-
#!/bin/bash
-
var0=0
-
LIMIT=10
-
while [ «$var0« -lt «$LIMIT« ]
-
do
-
echo -n «$var0 « # -n подавляет перевод строки.
-
var0=`expr $var0 + 1` # допускается var0=$(($var0+1)).
-
done
-
echo
-
exit 0
Пример 10-15. Другой пример цикла while
-
#!/bin/bash
-
echo
-
while [ «$var1« != «end» ] # возможна замена на while test «$var1» != «end»
-
do
-
echo «Введите значение переменной #1 (end — выход) «
-
read var1 # Конструкция ‘read $var1’ недопустима (почему?).
-
echo «переменная #1 = $var1« # кавычки обязательны, потому что имеется символ «#».
-
# Если введено слово ‘end’, то оно тоже выводится на экран.
-
# потому, что проверка переменной выполняется в начале итерации (перед вводом).
-
echo
-
done
-
exit 0
Оператор while может иметь несколько условий. Но только последнее из них определяет возможность продолжения цикла. В этом случае синтаксис оператора цикла должен быть несколько иным.
Пример 10-16. Цикл while с несколькими условиями
-
#!/bin/bash
-
var1=unset
-
previous=$var1
-
while echo «предыдущее значение = $previous«
-
echo
-
previous=$var1 # запомнить предыдущее значение
-
[ «$var1« != end ]
-
# В операторе «while» присутствуют 4 условия, но только последнее управляет циклом.
-
# *последнее* условие — единственное, которое вычисляется.
-
do
-
echo «Введите значение переменной #1 (end — выход) «
-
read var1
-
echo «текущее значение = $var1«
-
done
-
# попробуйте самостоятельно разобраться в сценарии works.
-
exit 0
Как и в случае с for, цикл while может быть записан в C-подобной нотации, с использованием двойных круглых скобок (см. так же Пример 9-29).
Пример 10-17. C-подобный синтаксис оформления цикла while
-
#!/bin/bash
-
# wh-loopc.sh: Цикл перебора от 1 до 10.
-
LIMIT=10
-
a=1
-
while [ «$a« -le $LIMIT ]
-
do
-
echo -n «$a «
-
let «a+=1»
-
done # Пока ничего особенного.
-
echo; echo
-
# +=================================================================+
-
# А теперь оформим в стиле языка C.
-
((a = 1)) # a=1
-
# Двойные скобки допускают наличие лишних пробелов в выражениях.
-
while (( a <= LIMIT )) # В двойных скобках символ «$» перед переменными опускается.
-
do
-
echo -n «$a «
-
((a += 1)) # let «a+=1»
-
# Двойные скобки позволяют наращивание переменной в стиле языка C.
-
done
-
echo
-
# Теперь, программисты, пишущие на C, могут чувствовать себя в Bash как дома.
-
exit 0
Стандартное устройство ввода stdin, для цикла while, можно перенаправить на файл с помощью команды перенаправления < в конце цикла.
-
- until
-
Оператор цикла until проверяет условие в начале каждой итерации, но в отличие от while итерация возможна только в том случае, если условие ложно.
until [condition-is-true]
do
command…
doneОбратите внимание: оператор until проверяет условие завершения цикла ПЕРЕД очередной итерацией, а не после, как это принято в некоторых языках программирования.
Как и в случае с циклами for/in, при размещении ключевого слова do в одной строке с объявлением цикла, необходимо вставлять символ «;» перед do.
until [condition-is-true] ; do
- Страница для печати
- 33181 просмотр