Основы BASH. Часть 2
Основы 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 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 — Стандартный вывод ошибок.
Для операций с этими дескрипторами, существуют специальные символы: > (перенаправление вывода), < (перенаправление ввода). Оперировать ими не сложно. Например:
записать в файл listing содержание текущего каталога (уже полезней)
Если есть необходимость дописывать в файл(при использовании «>» он заменятеся), необходимо вместо «>» использовать «>>»
после просьбы sudo ввести пароль, он возьмется из файла my_password, как будто вы его ввели с клавиатуры.
Если необходимо записать в файл только ошибки, которые могли возникнуть при работе программы, то можно использовать:
цифра 2 перед «>» означает что нужно перенаправлять все что попадет в дескриптор 2(stderr).
Если необходимо заставить stderr писать в stdout, то это можно можно след. образом:
символ «&» означает указатель на дескриптор 1(stdout)
(Поумолчанию stderr пишет на ту консоль, в котрой работает пользователь(вренее пишет на дисплей)).
2.Конвееры.
Конвеер — очень мощный инструмент для работы с консолью Bash. Синтаксис простой:
команда1 | команда 2 — означает, что вывод команды 1 передастся на ввод команде 2
Конвееры можно группировать в цепочки и выводить с помощью перенаправления в файл, например:
вывод команды ls -la передается команде grep, которая отбирает все строки, в которых встретится слово hash, и передает команде сортировке sort, которая пишет результат в файл sorting_list. Все довольно понятно и просто.
Чаще всего скрипты на Bash используются в качестве автоматизации каких-то рутинных операций в консоли, отсюда иногда возникает необходимость в обработке stdout одной команды и передача на stdin другой команде, при этом результат выполнения одной команды должен быть неким образом обработан. В этом разделе я постораюсь объяснить основные принципы работы с внешними командами внутри скрипта. Думаю что примеров я привел достаточно и можно теперь писать только основные моменты.
1. Передача вывода в переменную.
Для того чтобы записать в переменную вывод какой-либо команды, достаточно заключить команду в « ковычки, например
Однако если вы захотите записать в переменную список директорий, то необходимо, должным образом обработать результат для помещения данных в переменную. Рассмотрим небольшой, пример:
LIST=`find /svn/ -type d 2>/dev/null| awk ‘ ‘| 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 ‘ ‘| sort|uniq | tr ‘\n’ ‘ ‘` В ней переменной LIST присваивается выполнение команды find, обработанной командами awk, sort, uniq,tr(все эти команды мы рассматривать не будем, ибо это отдельная статья). В переменной LIST будут имена всех каталогов в папке /svn/ пгомещенных в одну строку(для того чтобы её стравить циклу.
Как видно, все не сложно, достаточно понять принцип и написать пару своих скриптов. В заключении статьи хочу пожелать удачи в изучении BASH и Linux в целом. Критика, как водится приветствуется. Следующая статья возможно будет посвещена использованию таких программ как sed, awk.
How do I write a ‘for’ loop in Bash?
It’s worth a mention that the range specified here is inclusive. By that, I mean you will see the entire range (1 to 10) printed to the console.
I prefer this solution since it does not use a seq so it does not generate a weird string containing a sequence of all the numbers, but it just loops efficiently over an integer.
The Bash for consists on a variable (the iterator) and a list of words where the iterator will, well, iterate.
So, if you have a limited list of words, just put them in the following syntax:
for w in word1 word2 word3 do doSomething($w) done
Probably you want to iterate along some numbers, so you can use the seq command to generate a list of numbers for you: (from 1 to 100 for example)
and use it in the for loop:
for n in $(seq 1 100) do doSomething($n) done
Note the $(. ) syntax. It’s a Bash behaviour, and it allows you to pass the output from one command (in our case from seq ) to another (the for ).
This is really useful when you have to iterate over all directories in some path, for example:
for d in $(find $somepath -type d) do doSomething($d) done
The possibilities are infinite to generate the lists.
Good answer, but you might want to include the for ((i=0; i
Bash 3.0+ can use this syntax:
. which avoids spawning an external program to expand the sequence (such as seq 1 10 ).
Of course, this has the same problem as the for(()) solution, being tied to Bash and even a particular version (if this matters to you).
Try the Bash built-in help:
Output
for: for NAME [in WORDS . ;] do COMMANDS; done The `for' loop executes a sequence of commands for each member in a list of items. If `in WORDS . ;' is not present, then `in "$@"' is assumed. For each element in WORDS, NAME is set to that element, and the COMMANDS are executed. for ((: for (( exp1; exp2; exp3 )); do COMMANDS; done Equivalent to (( EXP1 )) while (( EXP2 )); do COMMANDS (( EXP3 )) done EXP1, EXP2, and EXP3 are arithmetic expressions. If any expression is omitted, it behaves as if it evaluates to 1.
#! /bin/bash function do_something < echo value=$> MAX=4 for (( i=0; i >
Here’s an example that can also work in older shells, while still being efficient for large counts:
But good luck doing useful things inside of awk : How do I use shell variables in an awk script?
I didn’t notice it before, but a similar syntax is shown in one of the earlier answers. The unique thing here is the use of curly-braces instead of the typical do / done pair.
I commonly like to use a slight variant on the standard for loop. I often use this to run a command on a series of remote hosts. I take advantage of Bash’s brace expansion to create for loops that allow me to create non-numerical for loops.
I want to run the uptime command on frontend hosts 1-5 and backend hosts 1-3:
% for host in ,backend>.mycompany.com do ssh $host "echo -n $host; uptime" done
I typically run this as a single-line command with semicolons on the ends of the lines instead of the more readable version above. The key usage consideration are that braces allow you to specify multiple values to be inserted into a string (e.g. prepost results in prefoopost, prebarpost) and allow counting/sequences by using the double periods (you can use a..z, etc.). However, the double period syntax is a new feature of Bash 3.0; earlier versions will not support this.