{{tag>bash}}

====== Основы BASH. Часть 2. ======


===== Основы BASH. Часть =====

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


===== Циклы. Цикл 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"


А теперь результат работы скрипта:
  $ ./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" 


Результат выполнения скрипта:
  $ ./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 "


Результат выполнения:
  $ ./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
перенаправить вывод команды cat /dev/random в /dev/null (абсолютно бесполезная операция :)) ) или
ls -la > listing
записать в файл listing содержание текущего каталога (уже полезней)
Если есть необходимость дописывать в файл(при использовании ">" он заменятеся), необходимо вместо ">" использовать ">>"
sudo < my_password
после просьбы sudo ввести пароль, он возьмется из файла my_password, как будто вы его ввели с клавиатуры.
Если необходимо записать в файл только ошибки, которые могли возникнуть при работе программы, то можно использовать:
./program_with_error 2> error_file
цифра 2 перед ">" означает что нужно перенаправлять все что попадет в дескриптор 2(stderr).
Если необходимо заставить stderr писать в stdout, то это можно можно след. образом:
./program_with_error 2>&1
символ "&" означает указатель на дескриптор 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.

[[bash:основы_bash._часть_1|]]