Это одна из основных разновидностей циклов. И она
значительно отличается от аналога в языке 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 проверяет условие перед началом
каждой итерации и если условие истинно (если код возврата равен 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, можно перенаправить на файл с помощью
команды перенаправления < в конце цикла.
|