Введение в UNIX. Основы программирования в командной оболочке shell

  • 31.10.2019

Краткое описание разницы в типах циклов:

for — будет выполнять действие до тех пор, пока есть объекты для выполнения (например — чтение потока из stdin , файла или функции);
while — выполняет действие до тех пор, пока условие является истинным;
until — будет выполняться до тех пор, пока условие не станет истинным, т.е. пока оно false .

Цикл FOR

Рассмотрим такой вариант скрипта с циклом:

$ cat loop.sh #!/bin/bash for variable in `ls -1` do echo "$variable" done

Синтаксис очень простой и достаточно наглядно показан в примере:

for (запускаем цикл) variable (объявляем переменную, над которой будем выполнять действия) in (направляем циклу поток) `ls -1` (команда, которую необходимо выполнить и передать в переменную $variable). Do и done — «тело» цикла, в рамках которых будут выполняться основные действия над полученными данными, а echo "$variable" — непосредственно само действие, выполняемое циклом.

Теперь немного изменим пример, и вместо явного указания команды — применим вторую переменную:

$ cat loop.sh #!/bin/bash ls=`ls -1` for variable in $ls do echo "$variable" done

Теперь команда ls -1 передаётся в отдельной переменной, что позволяет более гибко работать с циклом. Вместо переменной в цикле можно использовать и функцию:

$ cat loop.sh #!/bin/bash lsl () { ls -1 } for variable in `lsl` do echo "$variable" done

Основное условие цикла for — он будет выполняться до тех пор, пока в переданной ему команде есть объекты для действия. Исходя из примера выше — пока в листинге ls -1 есть файлы для отображения — цикл будет передавать их в переменную и выполнять «тело цикла». Как только список файлов в директории закончится — цикл завершит своё выполнение.

Давайте немного усложним пример.

В каталоге имеется список файлов:

$ ls -1 file1 file2 file3 file4 file5 loop.sh nofile1 nofile2 nofile3 nofile4 nofile5

Нам необходимо выбрать из них только те, которые в названии не имеют слова «no «:

$ cat loop.sh #!/bin/bash lsl=`ls -1` for variable in $lsl do echo "$variable" | grep -v "no" done $ ./loop.sh file1 file2 file3 file4 file5 loop.sh

В цикле так же можно использовать условные выражения (conditional expressions ) […] для проверки условий и оператор break для прерывания цикла в случае срабатывания условия.

Рассмотрим такой пример:

$ cat loop.sh #!/bin/bash lsl=`ls -1` for variable in $lsl do if [ $variable != "loop.sh" ] then echo "$variable" | grep -v "no" else break fi done

Цикл будет выполняться до тех пор, пока не будет встречен файл loop.sh . Как только выполнение цикла дойдёт до этого файла — цикл будет прерван командой break:

$ ./loop.sh file1 file2 file3 file4 file5

Ещё один пример — использование арифметических операций непосредственно перед выполнением тела цикла:

$ cat loop.sh #!/bin/bash for ((count=1; count<11; count++)) do echo "$count" done

Тут мы задаём три управляющих команды — count=1 , контролирующее условие — пока count меньше 11, и команду для выполнения — count +1:

Циклы WHILE и UNTIL

Простой пример, хорошо демонстрирующий принцип работы цикла while:

$ cat loop.sh #!/bin/bash count=0 while [ $count -lt 10 ] do ((count++)) echo $count done

Мы задаём переменную $count равной нулю, после чего запускаем цикл whi le с условием «пока $count меньше десяти — выполнять цикл». В теле цикла мы выполняем постфиксный инкремент +1 к переменной $count и результат выводим в stdout .

Результат выполнения:

$ ./loop.sh 1 2 3 4 5 6 7 8 9 10

Как только значение переменной $count стало 10 — цикл прекратился.

Хороший пример «бесконечного» цикла, который демонстрирует работу while:

$ cat loop.sh #!/bin/bash count=10 while [ 1 = 1 ] do ((count++)) echo $count done $ ./loop.sh ... 5378 5379 5380 5381 5382 5383 ^C

Аналогично, но «в обратную сторону» работает и цикл until:

$ cat loop.sh #!/bin/bash count=0 until [ $count -gt 10 ] do ((count++)) echo $count done

Тут мы задаём похожее условие, но вместо «пока переменная меньше 10» — указываем «пока переменная не станет больше чем 10». Результат выполнения:

$ ./loop.sh 1 2 3 4 5 6 7 8 9 10 11

Если же приведённый выше пример «бесконечного цикла» выполнить с использованием until — о не выведет ничего, в отличии от while:

$ cat loop.sh #!/bin/bash count=10 until [ 1 = 1 ] do ((count++)) echo $count done $ ./loop.sh $

Так как «условие » изначально «истинно » — тело цикла выполняться не будет.

Как и в цикле for — в while и until можно использовать функции. Для примера — цикл из реально использующегося скрипта, выполняющий проверку статуса сервера Tomcat (PID берётся в системе SLES , в других системах может отличаться), немного упрощенный вариант:

$ cat loop.sh #!/bin/bash check_tomcat_status () { RUN=`ps aux | grep tomcat | grep -v grep | grep java | awk "{print $2}"` } while check_tomcat_status do if [ -n "$RUN" ] then printf "WARNING: Tomcat still running with PID $RUN." else printf "Tomcat stopped, proceeding...nn" break fi done

Результат выполнения:

$ ./loop.sh WARNING: Tomcat still running with PID 14435 26548.WARNING: Tomcat still running with PID 14435 26548.WARNING: Tomcat still running with PID 14435 26548.WARNING: Tomcat still running with PID 14435 26548.WARNING: Tomcat still running with PID 14435 26548.WARNING: Tomcat still running with PID 14435 26548.WARNING: Tomcat still running with PID 14435 26548.WARNING: Tomcat still running with PID 14435

Полный вариант:

Check_tomcat_status () { RUN=`ps aux | grep tomcat | grep -v grep | grep java | awk "{print $2}"` } while check_tomcat_status; do if [ -n "$RUN" ] then printf "WARNING: Tomcat still running with PID $RUN. Stop it? " answer "Stopping Tomcat..." "Proceeding installation..." && $CATALINA_HOME/bin/shutdown.sh 2&>1 /dev/null || break sleep 2 if [ -n "$RUN" ] then printf "Tomcat still running. Kill it? " answer "Killing Tomcat..." "Proceeding installation...n" && kill $RUN || break sleep 2 fi else printf "Tomcat stopped, proceeding...nn" break fi done

Функция answer описывалась в статье , но тут используется немного улучшенный вариант:

Answer () { while read response; do echo case $response in |) printf "$1n" return 0 break ;; |) printf "$2n" return 1 break ;; *) printf "Please, enter Y(yes) or N(no)! " esac done }

Тут можно было использовать как while , так и until — но не цикл for, так как for сработал бы один раз (получил PID — и завершился).

Дегтярев Е.К. Shell - интерпретатор команд, подаваемых с терминала или из командного файла. Это обычная программа (т.е. не входит в ядро операционной системы UNIX). Ее можно заменить на другую или иметь несколько. Две наиболее известные версии: - Shell (версии 7 UNIX) или Bourne Shell (от фамилии ав- тора S.R.Bourne из фирмы Bell Labs) ; - C-Shell (версии Berkley UNIX). Они похожи, но есть и отличия: C-Shell мощнее в диалого- вом режиме, а обычный Shell имеет более элегантные управляю- щие структуры. Shell - язык программирования, так как имеет: - переменные; - управляющие структуры (типа if); - подпрограммы (в том числе командные файлы); - передачу параметров; - обработку прерываний.

7.2. Файл начала сеанса (login - файл)

Независимо от версии Shell при входе в систему UNIX ищет файл начала сеанса с предопределенным именем, чтобы выпол- нить его как командный файл; - для UNIX версии 7 это: .profile; - для C-Shell это: .login и/или.cshrc. В этот файл обычно помещают команды: - установки характеристик терминала; - оповещения типа who, date; - установки каталогов поиска команд (обычно: /bin, /usr/bin); - смена подсказки с $ на другой символ и т.д.

7.3. Процедура языка Shell

Это командный файл. Два способа его вызова на выполнение: 1. $ sh dothat (где dothat - некоторый командный файл); 2. $ chmod 755 dothat (сделать его выполнимым, т.е. -rwxr-xr-x) $ dothat. Следует знать порядок поиска каталогов команд (по умолча- нию): - текущий; - системный /bin; - системный /usr/bin. Следовательно, если имя вашего командного файла дублирует имя команды в системных каталогах, последняя станет недос- тупной (если только не набирать ее полного имени).

7.4. Переменные Shell

В языке Shell версии 7 определение переменной содержит имя и значение: var = value. Доступ к переменной - по имени со знаком $ спереди: fruit = apple (определение); echo $fruit (доступ); apple (результат echo). Таким образом, переменная - это строка. Возможна конкате- кация строк: $ fruit = apple $ fruit = pine$fruit $ echo $fruit pineapple $ fruite = apple $ wine = ${fruite}jack $ echo $wine applejack $ Другие способы установки значения переменной - ввод из файла или вывод из команды (см. раздел 7.6), а также присва- ивание значений переменной - параметру цикла for из списка значений, заданного явно или по умолчанию (см. раздел 7.9). Переменная может быть: 1) Частью полного имени файла: $d/filename, где $d - пе- ременная (например, d = /usr/bin). 2) Частью команды: $ S = "sort + 2n + 1 - 2" (наличие пробелов требует кавы- чек "") $ $S tennis/lpr $ $S basketball/lpr $ $S pingpong/lpr $ Однако внутри значения для команды не могут быть символы |, >,

7.5. Предопределенные переменные Shell

Некоторые из них можно только читать. Наиболее употреби- тельные: HOME - "домашний" каталог пользователя; служит аргументом по умолчанию для cd; PATH - множество каталогов, в которых UNIX ищет команды; PS1 - первичная подсказка (строка) системы (для v.7 - $). Изменение PS1 (подсказки) обычно делается в login - фай- ле, например: PS1 = ? или PS1 = "? " (с пробелом, что удобнее). Изменение PATH: $ echo $PATH - посмотреть; :/bin:/usr/bin - значение PATH; $ cd - "домой"; $ mkdir bin - новый каталог; $ echo $HOME - посмотреть; /users/maryann - текущий каталог; $ PATH = :$HOME/bin:$PATH - изменение PATH; $ echo $PATH - посмотреть; :/users/maryann/bin:/bin:/usr/bin - новое значение PATH.

7.6. Установка переменной Shell выводом из команды

Пример 1: $ now = `date` (где `` - обратные кавычки) $ echo $now Sun Feb 14 12:00:01 PST 1985 $ Пример 2: (получение значения переменной из файла): $ menu = `cat food` $ echo $menu apples cheddar chardonnay (символы возврата каретки за- меняются на пробелы).

7.7. Переменные Shell - аргументы процедур

Это особый тип переменных, именуемых цифрами. Пример: $ dothis grapes apples pears (процедура). Тогда позиционные параметры (аргументы) этой команды дос- тупны по именам: $1 = `grapes` $2 = `apples` $3 = `pears` и т.д. до $9. Однако есть команда shift, которая сдвигает имена на остальные аргументы, если их больше 9 (окно шириной 9). Другой способ получить все аргументы (даже если их больше 9): $*, что эквивалентно $1$2 ... Количество аргументов присваивается другой переменной: $#(диез). Наконец, имя процедуры - это $0; переменная $0 не учитывается при подсчете $#.

7.8. Структурные операторы Shell

Кроме процедур, в языке Shell имеются структурные опера- торы типа "if-else" и "while-do". Программирование на Shell, кроме написания процедур, используется для: - отработки алгоритма перед кодированием его в языках С или ФОРТРАН-77 (нет компиляции, линкирования, загрузки, простота отладки); - обучения принципам программирования непрограммистов.

7.9. Оператор цикла for

Пусть имеется командный файл makelist (процедура) $ cat makelist sort +1 -2 people | tr -d -9 | pr -h Distribution | lpr. Если вместо одного файла people имеется несколько, нап- ример: adminpeople, hardpeople, softpeople,..., то необходимо повторить выполнение процедуры с различными файлами. Это возможно с помощью for - оператора. Синтаксис: for in do done Ключевые слова for, do, done пишутся с начала строки. Пример (изменим процедуру makelist) for file in adminpeople, hardpeople, softpeople do Sort +1 -2 $file | tr ... | lpr done. Можно использовать метасимволы Shell в списке значений. Пример: for file in *people (для всех имен, кончающихся на people) do ... done. Если in опущено, то по умолчанию в качестве списка значе- ний берется список аргументов процедуры, в которой содержит- ся цикл, а если цикл не в процедуре, то - список параметров командной строки (то есть в качестве процедуры выступает ко- манда). Пример: for file do ... done Для вызова makelist adminpeople hardpeople softpeople бу- дет сделано то же самое.

7.10. Условный оператор if

Используем имена переменных, представляющие значения па- раметров процедуры: sort +1 -2 $1 | tr ... | lpr Пример неверного вызова: makelist (без параметров), где $1 неопределен. Исправить ошибку можно, проверяя количество аргументов - значение переменной $# посредством if - оператора. Пример: (измененной процедуры makelist): if test $# -eq 0 then echo "You must give a filename" exit 1 else sort +1 -2 $1 | tr ... | lpr fi Здесь test и exit - команды проверки (см. раздел 7.11) и выхода. Таким образом, синтаксис оператора if: if ; then ; Ключевые слова if, then, else и fi пишутся с начала строки. Успешное выполнение процедуры означает, что она возвраща- ет значение true = 0 (zero) (неуспех - возвращаемое значение не равно 0). Оператор exit 1 задает возвращаемое значение 1 для неу- дачного выполнения makelist и завершает процедуру. Возможны вложенные if. Для else if есть сокращение elif, которое одновременно сокращает fi.

7.11. Команда "test"

Не является частью Shell, но применяется внутри Shell- процедур. Имеется три типа проверок: - оценка числовых значений; - оценка типа файла; - оценка строк. Для каждого типа свои примитивы (операции op). Для чисел синтаксис такой: N op M, где N, M - числа или числовые переменные; op принимает значения: -eq, -ne, gt, -lt, -ge, -le (с обычным смыслом, как, например, в ФОРТРАН). Для файла синтаксис такой: op filename, где op принимает значения: -s (файл существует и не пуст); -f (файл, а не каталог); -d (файл-директория (каталог); -w (файл для записи); -r (файл для чтения). Для строк синтаксис такой: S op R, где S, R - строки или строковые переменные или op1 S op принимает значения: = (эквивалентность); != (не эквивалентность); op1 принимает значения: -z (строка нулевой длины); -n (не нулевая длина строки). Наконец, несколько проверок разных типов могут быть объ- единены логическими операциями -a (AND) и -o (OR). Примеры: $ if test -w $2 -a -r S1 > then cat $1 >> $2 > else echo "cannot append" > fi $ В некоторых вариантах ОС UNIX вместо команды test исполь- зуются квадратные скобки, т.е. if [...] вместо if test ... .

7.12. Оператор цикла while

Синтаксис: while do done Если "команда" выполняется успешно, то выполнить "коман- ды", завершаемые ключевым словом done. Пример: if test $# -eq 0 then echo "Usage: $0 file ..." > &2 exit fi while test $# -gt 0 do if test -s $1 then echo "no file $1" > &2 else sort + 1 - 2 $1 | tr -d ... (процедуры) fi shift (* перенумеровать аргументы *) done Процедуры выполняются над всеми аргументами.

7.13. Оператор цикла until

Инвертирует условие повторения по сравнению с while Синтаксис: until do done Пока "команда" не выполнится успешно, выполнять команды, завершаемые словом done. Пример: if test S# -eq 0 then echo "Usage $0 file..." > &2 exit fi until test S# -eq 0 do if test -s $1 then echo "no file $1" > &2 else sort +1 -2 $1 | tr -d ... (процедура) fi shift (сдвиг аргументов) done Исполняется аналогично предыдущему.

7.14. Оператор выбора case

Синтаксис: case in string1) ;; string2) ;; string3) ... и т.д. ... esac Пример: Пусть процедура имеет опцию -t, которая может быть подана как первый параметр: ................. together = no case $1 in -t) together = yes shift ;; -?) echo "$0: no option $1" exit ;; esac if test $together = yes then sort ... fi где? - метасимвол (если -?, т.е. "другая" опция, отлич- ная от -t, то ошибка). Можно употреблять все метасимволы языка Shell, включая?, *, [-]. Легко добавить (в примере) другие опции, просто расширяя case.

7.15. Использование временных файлов в каталоге /tmp

Это специальный каталог, в котором все файлы доступны на запись всем пользователям. Если некоторая процедура, создающая временный файл, ис- пользуется несколькими пользователями, то необходимо обеспе- чить уникальность имен создаваемых файлов. Стандартный прием - имя временного файла $0$$, где $0 - имя процедуры, а $$ - стандартная переменная, равная уникальному идентификационно- му номеру процесса, выполняющего текущую команду. Хотя администратор периодически удаляет временные файлы в /tmp, хорошей практикой является их явное удаление после ис- пользования.

7.16. Комментарии в процедурах

Они начинаются с двоеточия:, которое считается нуль-ко- мандой, а текст комментария - ее аргументом. Чтобы Shell не интерпретировал метасимволы ($, * и т.д.), рекомендуется заключать текст комментария в одиночные кавычки. В некоторых вариантах ОС UNIX примечание начинается со знака #.

7.17. Пример процедуры

:"Эта процедура работает с файлами, содержащими имена" : "и номера телефонов," :"сортирует их вместе или порознь и печатает результат на" :"экране или на принтере" :"Ключи процедуры:" :"-t (together) - слить и сортировать все файлы вместе" :"-p (printer) - печатать файлы на принтере" if test $# - eq 0 then echo "Usage: $ 0 file ... " > & 2 exit fi together = no print = no while test $# -gt 0 do case $1 in -t) together = yes shift ;; -p) print = yes shift ;; -?) echo "$0: no option $1" exit ;; *) if test $together = yes then sort -u +1 -2 $1 | tr ... > /tmp/$0$$ if $print = no then cat /tmp/$0$$ else lpr -c /tmp/$0$$ fi rm /tmp/$0$$ exit else if test -s $1 then echo "no file $1" > &2 else sort +1 -2 $1 | tr...> /tmp/$0$$ if $print = no then cat /tmp/$0$$ else lpr -c /tmp/$0$$ fi rm /tmp/$0$$ fi shift fi;; esac done. Процедура проверяет число параметров $#, и если оно равно нулю, завершается. В противном случае она обрабатывает пара- метры (оператор case). В качестве параметра может выступать либо ключ (символ, предваряемый минусом), либо имя файла (строка, представленная метасимволом *). Если ключ отличен от допустимого (метасимвол? отличен от t и p), процедура завершается. Иначе в зависимости от наличия ключей t и p вы- полняются действия, заявленные в комментарии в начале проце- дуры.

7.18. Обработка прерываний в процедурах

Если при выполнении процедуры получен сигнал прерывания (от клавиши BREAK или DEL, например), то все созданные вре- менные файлы останутся неудаленными (пока это не сделает ад- министратор) ввиду немедленного прекращения процесса. Лучшим решением является обработка прерываний внутри про- цедуры оператором trap: Синтаксис: trap "command arguments" signals... Кавычки формируют первый аргумент из нескольких команд, разделенных точкой с запятой. Они будут выполнены, если воз- никнет прерывание, указанное аргументами signals (целые): 2 - когда вы прерываете процесс; 1 - если вы "зависли" (отключены от системы) и др. Пример (развитие предыдущего): case $1 in ..... *) trap "rm /tmp/*; exit" 2 1 (удаление временных файлов) if test -s $1 .............. rm /tmp/* Лучше было бы: trap "rm /tmp/* > /dev/null; exit" 2 1 так как прерывание может случиться до того, как файл /tmp/$0$$ создан и аварийное сообщение об этом случае пере- направляется на null-устройство.

7.19. Выполнение арифметических операций: expr

Команда expr вычисляет значение выражения, поданного в качестве аргумента и посылает результат на стандартный вы- вод. Наиболее интересным применением является выполнение операций над переменными языка Shell. Пример суммирования 3 чисел: $ cat sum3 expr $1 + $2 + $3 $ chmod 755 sum3 $ sum3 13 49 2 64 $ Пример непосредственного использования команды: $ expr 13 + 49 + 2 + 64 + 1 129 $ В expr можно применять следующие арифметические операто- ры: +, -, *, /, % (остаток). Все операнды и операции должны быть разделены пробелами. Заметим, что знак умножения следует заключать в кавычки (одинарные или двойные), например: "*", так как символ * имеет в Shell специальный смысл. Более сложный пример expr в процедуре (фрагмент): num = "wc -l

7.20. Отладка процедур Shell

Имеются три средства, позволяющие вести отладку процедур. 1) Размещение в теле процедуры команд echo для выдачи со- общений, являющихся трассой выполнения процедуры. 2) Опция -v (verbose = многословный) в команде Shell при- водит к печати команды на экране перед ее выполнением. 3) Опция -x (execute) в команде Shell приводит к печати команды на экране по мере ее выполнения с заменой всех пере- менных их значениями; это наиболее мощное средство.

  • Tutorial

Основы BASH. Часть 2.
Извиняюсь за такую большую задержку между статьями, но сессия дает о себе знать в самый неподходящий момент:)
Всем спасибо за замечания, критику и дополнения, которые были озвучены в комментариях к прошлой статье .
Эта часть, как и обещал, будет посвящена циклам, математическим операциям и использованию внешних команд.
Начнем.

Циклы. Цикл for-in.

Оператор for-in предназначен для поочередного обращения к значениям перечисленным в списке. Каждое значение поочередно в списке присваивается переменной.
Синтаксис следующий:
for переменная in список_значений
do
команды
done

Рассмотрим небольшой пример:

#!/bin/bash
for i in 0 1 2 3 4 #переменной $i будем поочередно присваивать значения от 0 до 4 включительно
do
echo "Console number is $i" >> /dev/pts/$i #Пишем в файл /dev/pts/$i(файл виртуального терминала) строку "Console number is $i"
done #цикл окончен
exit 0

После выполнения примера в первых 5 виртуальных консолях(терминалах) появится строка с её номером. В переменную $i поочередно подставляются значения из списка и в цикле идет работа со значением этой переменной

Циклы. Цикл while.

Цикл while сложнее цикла for-in и используется для повторения команд, пока какое-то выражение истинно(код возврата = 0).
Синтаксис оператора следующий:
while выражение или команда возвращающая код возврата
do
команды
done

Пример работы цикла рассмотрим на следующем примере:

#!/bin/bash
again=yes #присваиваем значение "yes" переменной again
while [ "$again" = "yes" ] #Будем выполнять цикл, пока $again будет равно "yes"
do
echo "Please enter a name:"
read name
echo "The name you entered is $name"

Echo "Do you wish to continue?"
read again
done
echo "Bye-Bye"


А теперь результат работы скрипта:
ite@ite-desktop:~$ ./bash2_primer1.sh
Please enter a name:
ite
The name you entered is ite
Do you wish to continue?
yes
Please enter a name:
mihail
The name you entered is mihail
Do you wish to continue?
no
Bye-Bye

Как видим цикл выполняется до тех пор, пока мы не введем что-то отличное от «yes». Между do и done можно описывать любые структуры, операторы и т.п., все они будут выполнятся в цикле.Но следует быть осторожным с этим циклом, если вы запустите на выполнение в нём какую-либо команду, без изменения переменной выражения, вы можете попасть в бесконечный цикл.
Теперь об условии истинности. После while, как и в условном операторе if-then-else можно вставлять любое выражение или команду, которая возвращает код возврата, и цикл будет исполнятся до тех пор, пока код возврата = 0! Оператор "[" аналог команды test, которая проверяет истинность условия, которое ей передали.

Рассмотрим еще один пример, я взял его из книги Advanced Bash Scripting. Уж очень он мне понравился:), но я его немного упростил. В этом примере мы познакомимся с еще одним типом циклов UNTIL-DO . Эта практически полный аналог цикла WHILE-DO, только выполняется пока какое-то выражение ложно.
Вот пример:

#!/bin/bash
echo "Введите числитель: "
read dividend
echo "Введите знаменатель: "
read divisor

Dnd=$dividend #мы будем изменять переменные dividend и divisor,
#сохраним их знания в других переменных, т.к. они нам
#понадобятся
dvs=$divisor
remainder=1

Until [ "$remainder" -eq 0 ]
do
let "remainder = dividend % divisor"
dividend=$divisor
divisor=$remainder
done

Echo "НОД чисел $dnd и $dvs = $dividend"


Результат выполнения скрипта:
ite@ite-desktop:~$ ./bash2_primer3.sh
Введите числитель:
100
Введите знаменатель:
90
НОД чисел 100 и 90 = 10

Математические операции

Команда let.
Команда let производит арифметические операции над числами и переменными.
Рассмотрим небольшой пример, в котором мы производим некоторые вычисления над введенными числами:
#!/bin/bash
echo "Введите a: "
read a
echo "Введите b: "
read b

Let "c = a + b" #сложение
echo "a+b= $c"
let "c = a / b" #деление
echo "a/b= $c"
let "c <<= 2" #сдвигает c на 2 разряда влево
echo "c после сдвига на 2 разряда: $c"
let "c = a % b" # находит остаток от деления a на b
echo "$a / $b. остаток: $c "


Результат выполнения:
ite@ite-desktop:~$ ./bash2_primer2.sh
Введите a:
123
Введите b:
12
a+b= 135
a/b= 10
c после сдвига на 2 разряда: 40
123 / 12. остаток: 3

Ну вот, как видите ничего сложного, список математических операций стандартный:
+ - сложение
- - вычитание
* - умножение
/ - деление
** - возведение в степень
% - модуль(деление по модулю), остаток от деления
let позволяет использовать сокращения арифметических команд, тем самым сокращая кол-во используемых переменных. Например: a = a+b эквивалентно a +=b и т.д

Работа с внешними программами при написании shell-скриптов

Для начала немного полезной теории.
Перенаправление потоков.
В bash(как и многих других оболочках) есть встроенные файловые дескрипторы: 0 (stdin), 1 (stdout), 2 (stderr).
stdout - Стандартный вывод. Сюда попадает все что выводят программы
stdin - Стандартный ввод. Это все что набирает юзер в консоли
stderr - Стандартный вывод ошибок.
Для операций с этими дескрипторами, существуют специальные символы: > (перенаправление вывода), < (перенаправление ввода). Оперировать ими не сложно. Например:
перенаправить вывод команды cat /dev/random в /dev/null (абсолютно бесполезная операция:))) или
записать в файл listing содержание текущего каталога (уже полезней)
Если есть необходимость дописывать в файл(при использовании ">" он заменятеся), необходимо вместо ">" использовать ">>"
после просьбы sudo ввести пароль, он возьмется из файла my_password, как будто вы его ввели с клавиатуры.
Если необходимо записать в файл только ошибки, которые могли возникнуть при работе программы, то можно использовать:
./program_with_error 2> error_file
цифра 2 перед ">" означает что нужно перенаправлять все что попадет в дескриптор 2(stderr).
Если необходимо заставить stderr писать в stdout, то это можно можно след. образом:
символ "&" означает указатель на дескриптор 1(stdout)
(Поумолчанию stderr пишет на ту консоль, в котрой работает пользователь(вренее пишет на дисплей)).
2.Конвееры.
Конвеер - очень мощный инструмент для работы с консолью Bash. Синтаксис простой:
команда1 | команда 2 - означает, что вывод команды 1 передастся на ввод команде 2
Конвееры можно группировать в цепочки и выводить с помощью перенаправления в файл, например:
ls -la | grep «hash» |sort > sortilg_list
вывод команды ls -la передается команде grep, которая отбирает все строки, в которых встретится слово hash, и передает команде сортировке sort, которая пишет результат в файл sorting_list. Все довольно понятно и просто.

Чаще всего скрипты на Bash используются в качестве автоматизации каких-то рутинных операций в консоли, отсюда иногда возникает необходимость в обработке stdout одной команды и передача на stdin другой команде, при этом результат выполнения одной команды должен быть неким образом обработан. В этом разделе я постораюсь объяснить основные принципы работы с внешними командами внутри скрипта. Думаю что примеров я привел достаточно и можно теперь писать только основные моменты.

1. Передача вывода в переменную.
Для того чтобы записать в переменную вывод какой-либо команды, достаточно заключить команду в `` ковычки, например
a = `echo "qwerty"`
echo $a

Результат работы: qwerty


Однако если вы захотите записать в переменную список директорий, то необходимо, должным образом обработать результат для помещения данных в переменную. Рассмотрим небольшой, пример:
LIST=`find /svn/ -type d 2>/dev/null| awk "{FS="/"} {print $4}"| sort|uniq | tr "\n" " "`
for ONE_OF_LIST in $LIST
do
svnadmin hotcopy /svn/$ONE_OF_LIST /svn/temp4backup/$ONE_OF_LIST
done

Здесь мы используем цикл for-do-done для архивирование всех директорий в папке /svn/ с помощью команды svnadmin hotcopy(что в нашем случае не имеет никого значения, просто как пример). Наибольшй интерес вызывает строка: LIST=`find /svn/ -type d 2>/dev/null| awk "{FS="/"} {print $4}"| sort|uniq | tr "\n" " "` В ней переменной LIST присваивается выполнение команды find, обработанной командами awk, sort, uniq,tr(все эти команды мы рассматривать не будем, ибо это отдельная статья). В переменной LIST будут имена всех каталогов в папке /svn/ пгомещенных в одну строку(для того чтобы её стравить циклу.

Как видно, все не сложно, достаточно понять принцип и написать пару своих скриптов. В заключении статьи хочу пожелать удачи в изучении BASH и Linux в целом. Критика, как водится приветствуется. Следующая статья возможно будет посвещена использованию таких программ как sed, awk.

  • Tutorial

Основы BASH. Часть 2.
Извиняюсь за такую большую задержку между статьями, но сессия дает о себе знать в самый неподходящий момент:)
Всем спасибо за замечания, критику и дополнения, которые были озвучены в комментариях к прошлой .
Эта часть, как и обещал, будет посвящена циклам, математическим операциям и использованию внешних команд.
Начнем.

Циклы. Цикл for-in.

Оператор for-in предназначен для поочередного обращения к значениям перечисленным в списке. Каждое значение поочередно в списке присваивается переменной.
Синтаксис следующий:
for переменная in список_значений
do
команды
done

Рассмотрим небольшой пример:

#!/bin/bash
for i in 0 1 2 3 4 #переменной $i будем поочередно присваивать значения от 0 до 4 включительно
do
echo "Console number is $i" >> /dev/pts/$i #Пишем в файл /dev/pts/$i(файл виртуального терминала) строку "Console number is $i"
done #цикл окончен
exit 0

После выполнения примера в первых 5 виртуальных консолях(терминалах) появится строка с её номером. В переменную $i поочередно подставляются значения из списка и в цикле идет работа со значением этой переменной

Циклы. Цикл while.

Цикл while сложнее цикла for-in и используется для повторения команд, пока какое-то выражение истинно(код возврата = 0).
Синтаксис оператора следующий:
while выражение или команда возвращающая код возврата
do
команды
done

Пример работы цикла рассмотрим на следующем примере:

#!/bin/bash
again=yes #присваиваем значение "yes" переменной again
while [ "$again" = "yes" ] #Будем выполнять цикл, пока $again будет равно "yes"
do
echo "Please enter a name:"
read name
echo "The name you entered is $name"

Echo "Do you wish to continue?"
read again
done
echo "Bye-Bye"


А теперь результат работы скрипта:
ite@ite-desktop:~$ ./bash2_primer1.sh
Please enter a name:
ite
The name you entered is ite
Do you wish to continue?
yes
Please enter a name:
mihail
The name you entered is mihail
Do you wish to continue?
no
Bye-Bye

Как видим цикл выполняется до тех пор, пока мы не введем что-то отличное от «yes». Между do и done можно описывать любые структуры, операторы и т.п., все они будут выполнятся в цикле.Но следует быть осторожным с этим циклом, если вы запустите на выполнение в нём какую-либо команду, без изменения переменной выражения, вы можете попасть в бесконечный цикл.
Теперь об условии истинности. После while, как и в условном операторе if-then-else можно вставлять любое выражение или команду, которая возвращает код возврата, и цикл будет исполнятся до тех пор, пока код возврата = 0! Оператор "[" аналог команды test, которая проверяет истинность условия, которое ей передали.

Рассмотрим еще один пример, я взял его из книги Advanced Bash Scripting. Уж очень он мне понравился:), но я его немного упростил. В этом примере мы познакомимся с еще одним типом циклов UNTIL-DO . Эта практически полный аналог цикла WHILE-DO, только выполняется пока какое-то выражение ложно.
Вот пример:

#!/bin/bash
echo "Введите числитель: "
read dividend
echo "Введите знаменатель: "
read divisor

Dnd=$dividend #мы будем изменять переменные dividend и divisor,
#сохраним их знания в других переменных, т.к. они нам
#понадобятся
dvs=$divisor
remainder=1

Until [ "$remainder" -eq 0 ]
do
let "remainder = dividend % divisor"
dividend=$divisor
divisor=$remainder
done

Echo "НОД чисел $dnd и $dvs = $dividend"


Результат выполнения скрипта:
ite@ite-desktop:~$ ./bash2_primer3.sh
Введите числитель:
100
Введите знаменатель:
90
НОД чисел 100 и 90 = 10

Математические операции

Команда let.
Команда let производит арифметические операции над числами и переменными.
Рассмотрим небольшой пример, в котором мы производим некоторые вычисления над введенными числами:
#!/bin/bash
echo "Введите a: "
read a
echo "Введите b: "
read b

Let "c = a + b" #сложение
echo "a+b= $c"
let "c = a / b" #деление
echo "a/b= $c"
let "c <<= 2" #сдвигает c на 2 разряда влево
echo "c после сдвига на 2 разряда: $c"
let "c = a % b" # находит остаток от деления a на b
echo "$a / $b. остаток: $c "


Результат выполнения:
ite@ite-desktop:~$ ./bash2_primer2.sh
Введите a:
123
Введите b:
12
a+b= 135
a/b= 10
c после сдвига на 2 разряда: 40
123 / 12. остаток: 3

Ну вот, как видите ничего сложного, список математических операций стандартный:
+ - сложение
- - вычитание
* - умножение
/ - деление
** - возведение в степень
% - модуль(деление по модулю), остаток от деления
let позволяет использовать сокращения арифметических команд, тем самым сокращая кол-во используемых переменных. Например: a = a+b эквивалентно a +=b и т.д

Работа с внешними программами при написании shell-скриптов

Для начала немного полезной теории.
Перенаправление потоков.
В bash(как и многих других оболочках) есть встроенные файловые дескрипторы: 0 (stdin), 1 (stdout), 2 (stderr).
stdout - Стандартный вывод. Сюда попадает все что выводят программы
stdin - Стандартный ввод. Это все что набирает юзер в консоли
stderr - Стандартный вывод ошибок.
Для операций с этими дескрипторами, существуют специальные символы: > (перенаправление вывода), < (перенаправление ввода). Оперировать ими не сложно. Например:
перенаправить вывод команды cat /dev/random в /dev/null (абсолютно бесполезная операция:))) или
записать в файл listing содержание текущего каталога (уже полезней)
Если есть необходимость дописывать в файл(при использовании ">" он заменятеся), необходимо вместо ">" использовать ">>"
после просьбы sudo ввести пароль, он возьмется из файла my_password, как будто вы его ввели с клавиатуры.
Если необходимо записать в файл только ошибки, которые могли возникнуть при работе программы, то можно использовать:
./program_with_error 2> error_file
цифра 2 перед ">" означает что нужно перенаправлять все что попадет в дескриптор 2(stderr).
Если необходимо заставить stderr писать в stdout, то это можно можно след. образом:
символ "&" означает указатель на дескриптор 1(stdout)
(Поумолчанию stderr пишет на ту консоль, в котрой работает пользователь(вренее пишет на дисплей)).
2.Конвееры.
Конвеер - очень мощный инструмент для работы с консолью Bash. Синтаксис простой:
команда1 | команда 2 - означает, что вывод команды 1 передастся на ввод команде 2
Конвееры можно группировать в цепочки и выводить с помощью перенаправления в файл, например:
ls -la | grep «hash» |sort > sortilg_list
вывод команды ls -la передается команде grep, которая отбирает все строки, в которых встретится слово hash, и передает команде сортировке sort, которая пишет результат в файл sorting_list. Все довольно понятно и просто.

Чаще всего скрипты на Bash используются в качестве автоматизации каких-то рутинных операций в консоли, отсюда иногда возникает необходимость в обработке stdout одной команды и передача на stdin другой команде, при этом результат выполнения одной команды должен быть неким образом обработан. В этом разделе я постораюсь объяснить основные принципы работы с внешними командами внутри скрипта. Думаю что примеров я привел достаточно и можно теперь писать только основные моменты.

1. Передача вывода в переменную.
Для того чтобы записать в переменную вывод какой-либо команды, достаточно заключить команду в `` ковычки, например
a = `echo "qwerty"`
echo $a

Результат работы: qwerty


Однако если вы захотите записать в переменную список директорий, то необходимо, должным образом обработать результат для помещения данных в переменную. Рассмотрим небольшой, пример:
LIST=`find /svn/ -type d 2>/dev/null| awk "{FS="/"} {print $4}"| sort|uniq | tr "\n" " "`
for ONE_OF_LIST in $LIST
do
svnadmin hotcopy /svn/$ONE_OF_LIST /svn/temp4backup/$ONE_OF_LIST
done

Здесь мы используем цикл for-do-done для архивирование всех директорий в папке /svn/ с помощью команды svnadmin hotcopy(что в нашем случае не имеет никого значения, просто как пример). Наибольшй интерес вызывает строка: LIST=`find /svn/ -type d 2>/dev/null| awk "{FS="/"} {print $4}"| sort|uniq | tr "\n" " "` В ней переменной LIST присваивается выполнение команды find, обработанной командами awk, sort, uniq,tr(все эти команды мы рассматривать не будем, ибо это отдельная статья). В переменной LIST будут имена всех каталогов в папке /svn/ пгомещенных в одну строку(для того чтобы её стравить циклу.

Как видно, все не сложно, достаточно понять принцип и написать пару своих скриптов. В заключении статьи хочу пожелать удачи в изучении BASH и Linux в целом. Критика, как водится приветствуется. Следующая статья возможно будет посвещена использованию таких программ как sed, awk.