done
# Список аргументов не задан, поэтому цикл работает с переменной '[email protected]'
#+ (список аргументов командной строки, включая пробельные символы).
echo
exit 0
При создании списка аргументов, в цикле for допускается пользоваться подстановкой команд. См. Пример 12-39, Пример 10-10 и Пример 12-33.
Пример 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-10, Пример 25-11 и Пример 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.