Управляющие конструкции: "for, while ..."

Цикл for

http://www.opennet.ru/docs/for

http://citforum.ru/operating_systems/linux/HOWTO/Bash-Prog-Intro-7.shtml

Управление ходом выполнения цикла

Общий формат цикла:

fог  имя_переменной  in list

do

команда1

команда...

done

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

Итерация в программировании — организация обработки данных, при которой действия повторяются многократно, не приводя при этом к вызовам самих себя (в отличие от рекурсии).

Когда какое-то действие необходимо повторить большое количество раз, в программировании используются циклы. Например, нужно вывести 200 раз на экран текст «Hello, World!». Вместо двухсоткратного повторения одной и той же команды вывода текста часто создается цикл, который повторяется 200 раз и 200 раз выполняет то, что написано в теле цикла. Один шаг цикла и называется итерацией.

Если не вдаваться в подробности, то использование условного оператора if (ветвление) преследует цель обойти ту же проблему что и циклы, проблему ограниченной памяти, однако класический пример(исторически), это цикл for

Простой цикл for

Цикл for наиболее распространенный вариант цикла, возможо благодаря своей простоте, ниже показан элементарный (базовый) вариант цикла, этот цикл просто выводит на экран список, который состоит из “ 1 2 3 4 5”. Чтобы получить доступ к каждому значению списка используют переменную, в качестве параметра [имя_переменной] в нашем случае используется [имя_переменной] - loop.

$ pg for_i

#!/bin/sh

# for_i

for loop in 1 2 3 4 5

do

echo $loop

done

Одной строкой: $ for loop in 1 2 3 4 5; do echo $loop; done

Результат:

$ for_i 

1

2

3

4

5

$

У переменной есть значения находящиеся в списке, когда значение переменной находится в списке, цикл for выполняет все команды один раз и использует имя переменной для доступа к значению в списке. Использование списка (in list) не является обязательным и зачастую лишним, если вы не используете его, цикл for воспользуется позиционными параметрами командной строки, смотри ниже "Применение параметров вместе с циклом for"

И того три простых шага:

создать переменную: for [имя переменной] in [list значений] ;  

что делать?: do [команда]  переменная цикла ($[имя переменной]) ;

закрыть цикл: done

$ for loop in 1 2 3 4 5; do echo $loop; done

Вывод на экран строки списка

Ниже приводится цикл for, список которого содержит строку значений “orange red blue grey  12345”. Для каждой переменной указана команда echo, в качестве параметра имя_переменной указывается loop. Команда echo с помощью переменной $ loop, которая создается конструкцией for loop выводит на экран каждое значение списка до тех пор, пока список не окажется пустым, можно комбинировать строки (в данном случае речь идет о цикле loop (петля) или то название которое вы придумали).

$ cat forlist

#!/bin/sh

# forlist

for loop in orange red blue grey "1 2 3 4 5"

do

echo $loop

echo "this is the fruit $loop"

done

Результат:

$ forlist 

orange

this is the fruit orange

red

this is the fruit red

blue

this is the fruit blue

grey

this is the fruit grey

1 2 3 4 5

this is the fruit 1 2 3 4 5

PS: Переменной цикла в нашем случае $loop цикл присваивает значение последней обработанной итерации.

$ for loop in orange red blue grey; do echo $loop; echo "loop-chek $loop"; done

orange

loop-chek orange

red

loop-chek red

blue

loop-chek blue

grey

loop-chek grey

$ echo $loop

grey

$

Цикл со счетчиком

Зачем цикл со счетчиком?, для выполнения заданного количества итераций. В этом случае при инициализации счётчиков задаются начальные значения переменных или одной переменной и после каждого прохода цикла проверяется условие, если проверка возвращает истину, то начинается следующий проход цикла если нет заканчивается. В блоке <приращение счётчиков> значение наших переменных счётчиков обязательно должны изменятся (не обязательно в большую сторону) так чтобы при проверки условия рано или поздно мы получили значение лож, иначе цикл никогда не закончится. Очень удобный и главное привычный вариант, в случае если какую либо операцию нужно повторить заданное количество раз:

for (( <инициализация счётчиков>; <проверка условия>; <приращение счётчиков>))

do

<выполняемые команды>

done

$ for ((var=1; var <= LIMIT ; var++)); do echo $var; done


$ for ((var=1; var <= 7 ; var++)); do echo $var; done

1

2

3

4

5

6

7

$ for ((var=1; var <= 5 ; var++)); do echo $var; done

1

2

3

4

5

$ for ((var=1; var < 4 ; var++)); do echo $var; done

1

2

3

$ for ((var=3; var <= 4 ; var++)); do echo $var; done

3

4


Бесконечный цикл

 

$ for ((;;)); do echo "Бесконечный цикл: CTRL+C для выхода"; done

......................................................

Бесконечный цикл: CTRL+C для выхода

Бесконечный цикл: CTRL+C для выхода

Бесконечный цикл: CTRL+C для выхода

Бесконечный цикл: CTRL+C для выхода

Беск^C

$

Использование команды ls совместно с циклом for

Этот цикл оценивает команду ls интерпретатора shell и отображает сведения о файлах текущего каталога.

$ pg forls

#!/bin/sh

#forls

for loop in `ls`

do

echo $loop

done

Результат:

$ forls 

caseans

............

caseparam

ifdirect

ifseted

myfil

..........

myfile.bak

test_redirect

test_redirect1

zzzz.txt

Еще пример передачи циклу for какой либо команды:

$ for i in $(ls *.txt); do echo $i; done

blacklist.txt

grepp.txt

$ for i in $(ls *.txt); do cp $i $i.back; done

$ ls *.back

blacklist.txt.back  grepp.txt.back

Применение параметров вместе с циклом for

Если в цикле for опустить часть in list, тогда станут доступны позиционные параметры командной строки, которые становятся аргументами.  Этот подход аналогичен следующему:

for params in "$@" 

или

for params in "$*"

В котором цикл for обращается к специальному параметру ¨$@¨ или ¨$*¨ для получения аргументов из командной строки, или как сказано ранее можно просто не писать конструкцию in list: 

$ pg forparam2

#!/bin/sh

#forparam2

for params

do

echo "You supplied $params as a command line option"

done

$ forparam2 file1 file2 file3 

You supplied file1 as a command line option

You supplied file2 as a command line option

You supplied file3 as a command line option

$

**** Использование позиционных параметров при записи цикла одной строкой, наверно не получится.

Создание резервных копий файлов с помощью цикла for

Цикл for можно использовать для создания резервных копий файлов. При этом переменная просто добавляется к целевому аргументу команды ср. Ниже применяется переменная под названием BAK. Эта переменная добавляется к каждому имени целевого файла при использовании цикла с помощью команды ср. Список включает shell-команду ls.

$ pg forcp

#!/bin/sh

#forcp

mkdir "back"

BAK=".bak"

for loop in `ls`

do

echo "copying $loop to $loop$BAK"

cp $loop /media/sf_Share/scripts/back/$loop$BAK

done

$

Удаления выполняемые с помощью редактора sed

Еще пример совместного использования циклов и команд, где для удаления всех пустых строк применяется потоковый редактор sed. Выходной поток данных направляется в новые файлы с расширением .HOLD. Затем команда mv возвращает файлам их исходные имена.

$ pg forsed

#!/bin/sh

#forsed

for files in `ls LPSO*`

do

sed -e "/^$/d" $files>$files.HOLD

mv $files.HOLD $files

done

$

Посылка сигналов серверам с помощью цикла for

Ну а поскольку цикл for может обработать каждое слово списка, установим переменную для отображения названий некоторых серверов сети. Воспользуемся циклом for для посылки сигналов каждому из этих серверов.

$ pg forping

#!/bin/sh

#forping

HOSTS="ya.ru 8.8.8.8 google.com"

for loop in $HOSTS

do

ping -c 2 $loop

done

Одной строкой:

$ HOSTS="ya.ru google.com"; for loop in $HOSTS; do ping -c 2 $loop; done

Проверим:

$ forping 

PING ya.ru (213.180.204.3) 56(84) bytes of data.

64 bytes from www.yandex.ru (213.180.204.3): ttl=56 time=25.6 ms

64 bytes from www.yandex.ru (213.180.204.3): ttl=56 time=26.4 ms

--- ya.ru ping statistics ---

2 packets transmitted, 2 received, 0% packet loss, time 1001ms

rtt min/avg/max/mdev = 25.684/26.059/26.434/0.375 ms

PING 8.8.8.8 (8.8.8.8) 56(84) bytes of data.

64 bytes from 8.8.8.8: icmp_seq=1 ttl=55 time=25.2 ms

64 bytes from 8.8.8.8: icmp_seq=2 ttl=55 time=24.9 ms

--- 8.8.8.8 ping statistics ---

2 packets transmitted, 2 received, 0% packet loss, time 1001ms

rtt min/avg/max/mdev = 24.990/25.133/25.276/0.143 ms

PING google.com (185.5.161.121) 56(84) bytes of data.

64 bytes from cache.google.com (185.5.161.121): ttl=57 time=22.4 ms

64 bytes from cache.google.com (185.5.161.121): ttl=57 time=20.7 ms

--- google.com ping statistics ---

2 packets transmitted, 2 received, 0% packet loss, time 1001ms

rtt min/avg/max/mdev = 20.772/21.600/22.428/0.828 ms

Массовое преобразование

команда tr

Чтобы найти все файлы, которые начинаются символами “LPSO”, и преобразовать их содержимое в символы верхнего регистра, используются команды ls и cat. Команда ls отображает список файлов, а команда cat применяется для передачи списка утилите tr. Передаваемые файлы получают расширение .UC. Обратите внимание, что при использовании в цикле for команды ls применяются обратные кавычки.

$ pg forUC

#!/bin/sh

#forUC

for files in `ls LPSO*`

do

cat $files | tr "[a-z]" "[A-Z]" >$files.UC

done

$

Подсчет с помощью циклов

Команда expr применяется, если в циклы необходимо ввести счетчики. Ниже рассматривается пример, в котором цикл for обрабатывает файлы, а вывод и подсчет количества файлов осуществляется с помощью команды expr.

То же самое делается при помощи ls | wc -l 

$ pg forcount

#!/bin/sh

#forcount

counter=0

for files in *

do

# increment

counter=`expr $counter + 1`

done

echo "There are $counter files in `pwd` we need to process"





$ forcount 

There are 62 files in /media/sf_Share/scripts we need to process

$ ls | wc -l

62

Цикл while

Цикл while(до тех пор пока, или просто "пока") выполняет ряд команд до тех пор, пока истинно условие, или лучше сказать цикл повторяет свое "тело" пока условие истинно. Этот цикл используется также для просмотра данных из файла ввода. Формат цикла while:

while команда 

do

команды1

команды2

...................

done

Между конструкциями while и do находится несколько команд, хотя в общем случае применяется только одна команда. Обычно команда выполняет проверку условия.

Команды, размещенные между ключевыми словами do и done, выполняются только в том случае, если код завершения command равен нулю; если код завершения принимает какое‑либо другое значение, цикл заканчивается.

Когда команды выполнены, контроль передается обратно, в верхнюю часть цикла. И все начинается снова до тех пор, пока проверяемое условие отлично от нуля.

Простой цикл while

Ниже приводится основная форма цикла while. Условие тестирования состоит в том, что если "COUNTER is less than 5", условие останется истинным. 

Переменная counter имеет начальное значение нуль, и ее значение увеличивается на постоянную величину при выполнении цикла.

$ ~ cat > whilecount

#!/bin/bash

# whilecount

 COUNTER=0

#счетчик равен? -5

 while [ $COUNTER -lt 5 ]

 do

#прибавление к счетчику единицы

 COUNTER=`expr $COUNTER + 1`

 echo $COUNTER

 done

^C

$ ~ export PATH=$PATH:~

$ ~ chmod 744 whilecount

$ ~ whilecount

1

2

3

4

5

$ ~

Указанный сценарий выводит на экран числа от 1 до 5, затем завершает работу.

Применение цикла while при вводе с клавиатуры

Цикл while может применяться для ввода информации с клавиатуры. В следующем примере введенная информация присваивается переменной film. Если нажать клавиши [Ctrl+D], цикл завершает выполнение.

$ cat > whileread

#!/bin/bash

# whileread

 echo " type <CTRL+D> to terminate"

 echo -n "enter your most liked film :"

while read FILM

do

 echo "Чувак фильм: \"$FILM\" это полный отстой!"

done

^C

$ chmod +x whileread

$ ./whileread

 type <CTRL+D> to terminate

enter your most liked film :Незнайка на луне

Чувак фильм: "Незнайка на луне" это полный отстой!

<CTRL+D>

$ ~

Команда break: Команде break может быть передан необязательный параметр. Команда break без параметра прерывает тот цикл, в который она вставлена, а break N прерывает цикл, стоящий на N уровней выше, причем 1-й уровень -- это уровень текущего цикла,

 $ break --help:

break: break [n]

    Exit for, while, or until loops.

    Exit a FOR, WHILE or UNTIL loop.  If N is specified, break N enclosing

    loops.

    Exit Status:

    The exit status is 0 unless N is not greater than or equal to 1.


$ cat whileread

#!/bin/bash

# whileread

 echo " type <CTRL+D> to terminate"

 echo -n "enter your most liked film :"

while read FILM

do

 echo "Чувак фильм: \"$FILM\" это полный отстой!"

    break

done

Счетчик в while

В общем случае:

x=1

while [ $x -lt N ]

do

echo "................ $x"

x=$(( $x + 1 ))

done

Отредактируем предыдущий сценарий:

$ vim whileread_count

#!/bin/bash

# whileread_count

 echo " type <CTRL+D> to terminate"

 echo -n "enter your most liked film :"

 x=1

while read FILM

[ $x -lt 4 ]

do

 echo "Чувак фильм: \"$FILM\" это полный отстой!"

 echo -n "enter your most liked film :"

 x=$(( $x + 1 ))

done

echo "Ступай лесом"


$ ./whileread_count

 type <CTRL+D> to terminate

enter your most liked film :Незнайка на Луне

Чувак фильм: "Незнайка на Луне" это полный отстой!

enter your most liked film :Красавица и чудовище

Чувак фильм: "Красавица и чудовище" это полный отстой!

enter your most liked film :Маугли

Чувак фильм: "Маугли" это полный отстой!

enter your most liked film :НЕ ЗНАЮ

Ступай лесом

$  

Применения цикла while для считывания данных из файлов

Обычно цикл while используется для считывания данных из файла, что позволяет сценарию обрабатывать информацию.

Цикл while — лучший способ построчного чтения файла. Если вам необходимо построчно прочитать файл и выполнить какое-либо действие с каждой его строкой, тогда самым правильным способом для этого является использование конструкции while read line.

Общая структура конструкции while read line, которая может использоваться в Bash скриптах:

#!/bin/bash

#<NAME>

while read LINE

do COMMAND

done < FILE

Та же конструкция одной строкой в CLI:

$ ~ while read LINE; do COMMAND; done < FILE

Или предыдущий пример, способ заключается в разделении конструкции символом (;) - разделитель команд который и позволяет записать более одной команды в строке:

$ ~ echo " type <CTRL+D> to terminate"; echo -n "enter your most liked film :"; while read FILM; do  echo "Чувак фильм: \"$FILM\" это полный отстой!"; done



В качестве еще одного примера выведем список всех пользователей из файла /etc/passw

$ ~ while read LINE; do echo "$LINE" | cut -f1 -d":"; done < /etc/passwd

root

bin

daemon

mail

........................................

$ ~

Для хранения строк данных можно использовать переменные. Условие истинно до тех пор, пока не считываются новые данные. Для просмотра содержимого файла цикл while использует перенаправление потока данных ввода. LINE в этой конструкции — это название переменной, в которой сохраняется значение текущей строки во время каждой итерации цикла. В зависимости от содержимого вашего файла, (для удобства) можно назвать эту переменную как угодно.

$ cat > users.txt

Маша

Петя

Вася

Гриша

^C

$ while read USER; do echo "Привет $USER!"; done < users.txt

Привет Маша!

Привет Петя!

Привет Вася!

Привет Гриша!

Посмотреть скорость RX/TX интерфейса в кбит/с (Tx - transfer / Rx - receive)


$ while [ /bin/true ]; do OLD=$NEW; NEW=`cat /proc/net/dev | grep wlan0 | tr -s ' ' | cut -d' ' -f "3 11"`; echo $NEW $OLD | awk '{printf("\rin: % 9.2g\t\tout: % 9.2g", ($1-$3)/1024, ($2-$4)/1024)}'; sleep 1; done

in:        15 out:       1.3

^C

$ while true; do cat /proc/net/dev; sleep 1; done | awk -v dc="date \"+%T\"" '/wlan0/{i = $2 - oi; o = $10 - oo; oi = $2; oo = $10; dc|getline d; close(dc); if (a++) printf "%s %8.2f KiB/s in %8.2f KiB/s out\n", d, i/1024, o/1024}'

11:44:54     0.45 KiB/s in     0.33 KiB/s out

11:44:55     0.32 KiB/s in     0.50 KiB/s out

11:44:56     0.78 KiB/s in     0.70 KiB/s out

11:44:57     0.32 KiB/s in     0.33 KiB/s out

11:44:58     0.32 KiB/s in     0.33 KiB/s out

11:44:59     0.32 KiB/s in     0.33 KiB/s out

11:45:00     4.51 KiB/s in     3.33 KiB/s out

11:45:01     0.92 KiB/s in     0.61 KiB/s out

11:45:02     0.32 KiB/s in     0.33 KiB/s out

11:45:03     0.49 KiB/s in     0.48 KiB/s out

^C

Циклы в Ruby

Возможно будет интересно сравнить с "организацией" циклов в Ruby

==============================================================

К числу наиболее распространенных идиом (устойчивые конструкции) принадлежат формы записи циклов.

    i = о;

    while (i <= n-1)

        array[i++) = 1.0;

    Возможен и такой вариант:

    for (i = О; i < n; )

        array(i++) = 1.0;

    Не исключена также подобная форма записи:

    for (i = n; --i >= О; )

        array[i) = 1.0;

    Все эти формы правильны, но идиоматической, устойчивой конструкцией явля­ется только следующая:

    for (i = О; i < n; i++)

        array[i] = 1.0;

==============================================================