Wiki

Оператор цикла с перечислением ("for")

Оператор цикла "for" имеет структуру:

    for имя [in список значений]
	do
	    список команд
	done

где "for" - служебное слово определяющее тип цикла, "do" и "done" - служебные слова, выделяюшие тело цикла. Не забывайте про
"done"! Фрагмент "in список значений" может отсутствовать.

Пусть команда "lsort" представлена командным файлом

     for i in f1 f2 f3
     do
       proc-sort $i
     done

В этом примере имя "i" играет роль параметра цикла. Это имя можно рассматривать как shell-переменную, которой последовательно присваиваются перечисленные значения (i=f1, i=f2, i=f3), и выполняется в цикле команда "procsort".

Часто используется форма "for i in *", означающая "для всех файлов текущего каталога".

Пусть "proc-sort" в свою очередь представляется командным файлом

   cat $1 | sort | tee /dev/lp > ${1}_sorted

т.е. последовательно сортируются указанные файлы, результаты сортировки выводятся на печать ("/dev/lp") и направляются в файлы
   f1_sorted  f2_sorted  и  f3_sorted

Можно сделать более универсальной команду "lsort", если не фиксировать перечень файлов в команде, а передавать произвольное их число параметрами.

Тогда головная программа будет следующей:

   for i
       do
	 proc-sort $i
       done

Здесь отсутствие после "i" служебного слова "in" с перечислением имен говорит о том , что список поступает через параметры команды. Результат предыдущего примера можно получить, набрав
   lsort f1 f2 f3

Усложним ранее рассматривавшуюся задачу (под именем "case-2") определения холдинга фирмы. Теперь можно при вызове указывать произвольное количество фирм. При отсутствии в структуре оператора "for" фрагмента "in список значений", значения берутся из параметров вызывающей команды.
     ###
     # holding: Справочник.
     #          Для различных фирм по имени выдается
     #          название холдинга, в который она входит
     for i
	 do
	     case $i in
			 ONE|TWO|THREE) echo Холдинг: ZERO    ;;
			       MMM|WWW) echo Холдинг: Not-Net ;;
		     Hi|Hello|Howdoing) echo Холдинг: Привет! ;;
				     *) echo Нет такой фирмы  ;;
	     esac
	 done

При вызове "holding Hello HELLO ONE" на экране будет:
     Холдинг: Привет!
     Нет такой фирмы
     Холдинг: Not-Net

Еще пример.
     ###
     # subdir: Выдает имена всех поддиректориев
     #         директория с именем $dir
 
	     for i in $dir/*
	     do
		if [ -d  $i ]
		   then echo $i
		fi
	     done

Следующий расчет иллюстрирует полезный, хотя и с долей трюкачества, способ повторения одних и тех же действий несколько раз. Переменныя "i" принимает здесь пять значений: 1, 2, 3, 4, 5, но внутри цикла эта переменная отсутствует и поэтому ее значение никакой роли не играет и ни чего не меняет. С таким же успехом переменная "i" могла принимать значения, скажем ф о к у с , а в результате точно также было бы пять раз повторено одно и то же вычисление содержимого цикла без изменений.
     ###
     # print-5: Организации пятикратного выполнения команды
 
     for i in 1 2 3 4 5
	do
	   cat file-22 > /dev/lp
	done

Расчет "print-n" иллюстрирует еще одну полезную возможность в использовании цикла "for". Здесь, после "for i ...", отсутствуют "in ..." и перечень имен, т.е. перечнем имен для "i" становится перечень параметров, а следовательно количество печатаемых экземпляров можно менять.
     ###
     # print-n: Задание числа копий
     #          через параметры
 
     for i
	  do
		  cat file-22 > /dev/lp
	  done

Смысл не изменится, если первую строку расчета записать как
	for i in $*

поскольку значение "$*" - есть список значений параметров.

Отметим различие в специальных переменных "$*" и "$@", представляющих перечень параметров. Первый представляет параметры, как строку, а второй, как совокупность слов.

Пусть командный файл "cmp" имеет вид:

     for i in "$*"
       do
	   echo $i
       done
     echo
     for i in "$@"
	do
	   echo $i
	done

При вызове
     cmp aa bb cc

на экран будет выведено
     aa bb cc
 
     aa
     bb
     cc

5.4. Оператор цикла с истинным условием ("while")

Структура "while", также обеспечивающая выполнение расчетов, предпочтительнее тогда, когда неизвестен заранее точный список значений параметров или этот список должен быть получен в результате вычислений в цикле.

Оператор цикла "while" имеет структуру:

    while условие
	do
	    список команд
	done

где "while" - служебное слово определяющее тип цикла с истинным условием. Список команд в теле цикла (между "do" и "done") повторяется до тех пор, пока сохраняется истинность условия (т.е. код завершения последней команды в теле цикла равен "0") или цикл не будет прерван изнутри специальными командами ("break", "continue" или "exit"). При первом входе в цикл условие должно выполняться.
     ###
     # print-50: Структура "while"
     #           Расчет позволяет напечатать 50
     #           экземпляров файла "file-22"
     n=0
     while [ $n -lt 50 ]    # пока n < 50
     do
	n=`expr $n + 1`
	cat file-22 > /dev/lp
     done

Обратим внимание на то, что переменной "n" вначале присваивается значение 0, а не пустая строка, так как команда "expr" работает с shell-переменными как с целыми числами, а не как со строками.
     n=`expr $n + 1`

т.е. при каждом выполнении значение "n" увеличивается на 1.

Как и вообще в жизни, можно реализовать то же самое и сложнее. Расчет "рr-br" приведен для иллюстрации бесконечного цикла и использования команды "break", которая обеспечивает прекращение цикла.

     ###
     # рr-br: Структура "while" c "break"
     #        Расчет позволяет напечатать 50
     #        экземпляров файла "file-22"
     n=0
     while true
     do
	if [ $n -lt 50 ]           # если n < 50
	   then n=`expr $n + 1`
	   else break
	fi
	     cat file-22 > /dev/lp
     done

Команда "break [n]" позволяет выходить из цикла. Если "n" отсутствует, то это эквивалентно "break 1". "n" указывает число вложенных циклов, из которых надо выйти, например, "break 3" - выход из трех вложенных циклов.

В отличие от команды "break" команда "continue [n]" лишь прекращает выполнение текущего цикла и возвращает на НАЧАЛО цикла. Она также может быть с параметром. Например, "continue 2" означает выход на начало второго (если считать из глубины) вложенного цикла.

Команда "exit [n]" позволяет выйти вообще из процедуры с кодом возврата "0" или "n" (если параметр "n" указан). Эта команда может использоваться не только в циклах. Даже в линейной последовательности команд она может быть полезна при отладке, чтобы прекратит выполнение (текущего) расчета в заданной точке.