Управляющие конструкции: "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
$
Массовое преобразование
Чтобы найти все файлы, которые начинаются символами “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
........................................
$ ~
Для хранения строк данных можно использовать переменные. Условие истинно до тех пор, пока не считываются новые данные. Для просмотра содержимого файла цикл 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;
==============================================================