Введение в UNIX. Безусловная передача управления


Дегтярев Е.К. 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.

Что такое shell и зачем он нужен

Командная оболочка в любых unix-подобных системах, к которым относится и GNU/Linux, является обычной программой, запускаемой как в текстовой консоли (которая используется всё реже), так и в графической среде – в окне эмулятора терминала, доступного в любой Linux-системе.

Ее задача проста и очевидна: принять строку (или строки) ввода, произвести их синтаксический анализ и на основе результатов этого анализа отреагировать соответствующим образом – выполнить команду, запустить программу, вывести диагностическое сообщение и т.п.

Почти во всех дистрибутивах Linux для пользователей по умолчанию назначается командная оболочка bash (Bourne Again SHell – ещё одна командная оболочка Бурна; Стив Бурн – автор первой командной оболочки в Unix – sh). Фактически она стала неофициальным стандартом, и усовершенствование ее функциональных возможностей продолжается непрерывно. Существуют и другие командные оболочки – tcsh (версия C-shell), ksh (Korn Shell), zsh и т.д. – у каждой есть свои достоинства и недостатки, а также свои группы поклонников. Тем не менее, bash более привычна широким массам пользователей с различными уровнями подготовки, потому я и остановил свой выбор на ней. Стоит также отметить, что какими бы возможностями ни обладали различные оболочки, все они совместимы со своим идеологическим прародителем – Bourn Shell (sh). Иными словами, скрипт, написанный для sh, будет корректно работать в любой современной оболочке (обратно, вообще говоря, неверно).

Преимущества командной строки

Может возникнуть вопрос: зачем возиться с командной строкой, если существуют удобные и красивые графические интерфейсы? Тому есть множество причин. Во-первых, далеко не все операции удобнее и быстрее выполнять с помощью графического интерфейса. Во-вторых, каждая программа следует основополагающему принципу Unix-систем: делать чётко определённую работу и делать её хорошо. Иными словами, вы всегда понимаете, что происходит при запуске той или иной утилиты (если что-то не вполне понятно, то следует обратиться к man-руководству). В-третьих, осваивая команды, пробуя их сочетания и комбинации их параметров, пользователь изучает систему, приобретая ценный практический опыт. Вы получаете доступ к таким эффективным инструментам, как конвейеры, позволяющие организовать цепочку команд для обработки данных, средства перенаправления ввода/вывода, а кроме того, можете программировать непосредственно в командной оболочке. Пожалуй, на программировании стоит остановиться подробнее, тем более что многие системные сценарии в Linux (например, скрипты запуска системных сервисов) написаны для shell.

Командная оболочка в качестве языка программирования

Итак, командную оболочку можно рассматривать как язык программирования и как программную среду выполнения одновременно. Разумеется, этот язык не компилируемый, а интерпретируемый. Он допускает использование переменных: системных или собственных. Последовательность выполнения команд программы изменяется с помощью конструкций проверки условия и выбора соответствующего варианта: if-then-else и case. Циклы while, until и for позволяют автоматизировать многократно повторяющиеся действия. Имеется возможность объединять группы команд в логические блоки. Вы можете даже писать настоящие функции с передачей в них параметров. Таким образом, налицо все признаки и характеристики полноценного языка программирования. Попробуем извлечь из этого двойную пользу – наряду с изучением основ программирования автоматизируем свою повседневную работу.

Hello, World! Простая система резервного копирования

О необходимости регулярного резервного копирования данных знают все, но у пользователей вечно не хватает времени на эту скучную операцию. Выход прост – организовать автоматическое создание резервных копий. Это и будет нашим первым заданием по программированию в командной оболочке.

#!/bin/bash # # Резервное копирование каталогов и файлов из домашнего каталога # Этот командный скрипт можно автоматически запускать при помощи cron # cd $HOME if [ ! -d archives ] then mkdir archives fi cur_date=`date +%Y%m%d%H%M` if [ $# -eq 0 ] ; then tar czf archive${cur_date}.tar.gz projects bin else tar czf archive${cur_date}.tar.gz $* fi if [ $? = 0 ] ; then mv archive${cur_date}.tar.gz $HOME/archives echo "$cur_date – Резервное копирование успешно завершено." else echo "$cur_date – ОШИБКА во время резервного копирования." fi

Любой командный сценарий (script – скрипт, так называются программы командной оболочки) начинается со строки идентификатора, в которой явно задаётся интерпретатор команд с указанием полного пути к нему. Полный путь – последовательное перечисление всех каталогов, начиная с корневого, в которые надо войти, чтобы добраться до целевого файла, и, разумеется, имя этого файла. Запись полного пути чрезвычайно важна для однозначной идентификации каждого файла в иерархии файловой системы.

Далее следуют четыре строки комментариев. Как только командная оболочка встречает символ "#", она считает все последующие символы комментариями и полностью игнорирует их до конца текущей строки. Поэтому комментарий можно начать не с самого начала строки, а сопроводить им какую-либо команду.

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

Наконец-то мы добрались до первой «настоящей» команды. Она позволяет сменить каталог (Change Directory), т.е. перейти из текущего каталога в другой, переданный команде как аргумент. В большинстве случаев целевой каталог задаётся в явной форме, например, cd /tmp или cd projects, но в нашем случае используется предопределённая системная переменная HOME – в ней содержится полный путь к домашнему каталогу текущего пользователя, от имени которого выполняется командный сценарий. Тем самым мы избавляемся от необходимости вносить изменения в код всякий раз при смене пользователя, потому что команда возвращает любого в его личный каталог. Знак доллара "$" перед именем переменной означает, что необходимо извлечь значение, содержащееся в этой переменной, и подставить его в командную строку вместо её имени. Особо следует отметить, что в командном языке оболочки регистров букв имеют важное значение, т.е. HOME, Home и home – это три различные переменные. По соглашению, буквами верхнего регистра обозначаются имена системных переменных: HOME, PATH, EDITOR и т.д. Это соглашение не запрещает пользователям создавать свои переменные с именами из заглавных букв, но зачем усложнять себе жизнь, нарушая общепринятые нормы и правила? Не рекомендуется также изменять значения системных переменных без крайней необходимости. В общем, соблюдаем простое правило: системные переменные используем только для чтения, а если потребовалась собственная, то её имя записываем буквами нижнего регистра.

Нашу первую команду можно было бы записать более кратко:

cd ~

Здесь символ "~" также означает домашний каталог текущего пользователя. Ветераны командной строки выражаются ещё лаконичнее:

cd

Смысл в том, что когда для команды cd не задан никакой аргумент, она выполняет переход в домашний каталог.

На очереди классическая программная конструкция проверки условия и принятия соответствующего решения. Общая схема такова:

if <условие> then <одна или несколько команд> fi

Последнее слово конструкции (if в обратном порядке) выполняет роль закрывающей скобки, т.е. границы списка команд, выполняемых при истинности условия. Присутствие fi обязательно, даже если в списке лишь одна команда.

Для проверки условия, как правило, применяется команда test или её альтернативная форма записи в квадратных скобках. Иначе говоря, записи

if [ ! -d archives ] if test ! -d archives

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

Критерии проверки условия определяются разнообразными флагами. Команда test распознаёт очень большой их список. В нашем примере использован флаг -d, позволяющий проверить, соответствует ли заданное после флага имя реально существующему каталогу (directory). Наиболее часто при работе с файлами применяются следующие флаги:

F – существует ли обычный файл с заданным именем;

R – установлено ли для заданного файла право на чтение из него;

W – установлено ли для заданного файла право на запись в него;

X – установлено ли для заданного файла право на его выполнение;

S – имеет ли заданный файл ненулевой размер.

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

if [ ! -d archives ] Если не существует каталог archives (в текущем каталоге), then то начать выполнение блока команд: mkdir archives создать каталог archives (в текущем каталоге) fi завершить выполнение блока команд.

Как видите, всё оказалось не таким уж и сложным. Немного практики, и вы без труда сможете читать и самостоятельно создавать подобные конструкции. Команда создания каталога настолько очевидна, что дополнительных разъяснений не требуется.

В следующей строке мы создаём собственную локальную переменную cur_date. В подавляющем большинстве случаев переменные создаются простым присваиванием конкретного значения, например:

ten=10 string="Это строка текста"

Но в нашем примере применяется небольшая хитрость. Обратите внимание, что после знака равенства – символа присваивания – записана команда в обратных кавычках. Такая форма записи позволяет присвоить переменной не саму строку, а результат её выполнения. Здесь это вывод команды date, которая возвращает текущую дату и время в формате, определяемом списком параметров:

%Y – текущий год в полной форме, т.е. из четырёх цифр (например, 2009);

%m – номер текущего месяца (например, 09 – для сентября);

%d – номер текущего дня;

%H – текущий час в 24-часовом формате;

%M – текущая минута.

Таким образом, если выполнить команду

cur_date=`date +%Y%m%d%H%M`

десятого сентября 2009 года в 22:45, то переменной cur_date будет присвоено строковое значение "200909102245". Цель этого ухищрения – сформировать уникальное, не повторяющееся имя архивного файла. Если вы намерены запустить несколько экземпляров программы в течение одной минуты, то можете улучшить уникальность имён, добавляя ещё и текущие секунды. Как? Изучите руководство утилиты date (man date) – в этом нет ничего сложного.

Прежде чем приступить к созданию файла архива, необходимо определить, какие именно каталоги мы будем сохранять в нём. Для большей гибкости можно задать набор каталогов, архивируемых по умолчанию, но предусмотреть возможность замены этого набора списком каталогов, передаваемым как аргумент в наш командный сценарий. Для этого используются специальные переменные командной оболочки: $# – число переданных в сценарий параметров и $* – все переданные параметры, записанные в формате одной строки.

if [ $# -eq 0 ] ; then

Проверка условия «если число переданных параметров равно нулю», то выполнить следующую команду. Отметим, что ключевое слово then можно записать в строке условия, отделив его от условного выражения точкой с запятой.

tar czf archive${cur_date}.tar.gz projects bin

Команда создания архивного файла и сжатия этого файла. Сама утилита tar не выполняет сжатие, а только лишь собирает все заданные файлы и каталоги в единый tar-файл. Для этого предназначен первый флаг - c (create – создать). Сжатие выполняет внешняя программа – здесь это gzip, вызываемый вторым флагом - z. Если в вашей системе установлена более эффективная программа сжатия bzip2, то вы можете воспользоваться ею, изменив команду следующим образом:

tar cjf archive${cur_date}.tar.bz2 projects bin

Третий флаг f сообщает о том, что далее следует имя архивного файла, поэтому всегда является замыкающим в перечне флагов. Обратите внимание на то, что при подстановке имя переменной заключено в фигурные скобки. Это сделано, чтобы явно выделить переменную в окружающей её строке, тем самым устраняя многие потенциальные проблемы. Расширения архивному файлу не присваиваются автоматически, вы сами дописываете всё необходимое. В качестве каталогов, архивируемых по умолчанию, я указал projects и bin, но вы можете записать здесь имена своих наиболее ценных каталогов.

Ключевое слово else открывает альтернативную ветвь выполнения. Команды этого блока начинают работать, если проверка условия даёт результат «ложь» (в нашем примере: «число переданных параметров ненулевое», т.е. пользователь задал имена каталогов). В этом случае команда будет выглядеть так:

tar czf archive${cur_date}.tar.gz $*

Здесь каталоги по умолчанию заменены строкой имён каталогов, принятой извне. Имеется возможность принимать и обрабатывать каждый внешний параметр по отдельности, но нам удобнее передать строку целиком.

В конце программы выполняется ещё одна проверка. В unix-средах все команды возвращают код статуса завершения своей работы. Если команда отработала успешно, то она возвращает код 0, в противном случае код завершения будет ненулевым. Чтобы проверить успешность выполнения предыдущей команды архивации, воспользуемся ещё одной специальной переменной $?, в которой всегда содержится значение кода завершения самой последней команды. Если в переменной $? содержится 0, т.е. файл резервной копии был успешно создан, то мы перемещаем его в каталог архивов:

mv archive${cur_date}.tar.gz $HOME/archives

и выдаём соответствующее сообщение:

echo "$cur_date – Резервное копирование успешно завершено."

Если проверка показала, что код завершения операции архивирования не равен нулю, то выводится сообщение об ошибке:

echo "$cur_date – ОШИБКА во время резервного копирования."

На этом работа нашего командного сценария завершается.

Чтобы проверить работу нашей программы, необходимо сохранить описанный выше исходный код в файле, например, с именем bckp, а затем для удобства сделать его выполняемым:

chmod 750 bckp

и запустить:

./bckp

для создания резервной копии каталогов, заданных по умолчанию, и

./bckp docs progs works

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

Можно поместить файл bckp в один из каталогов, указанных в системной переменной PATH. Наиболее предпочтительными местами размещения являются /usr/local/bin или $HOME/bin, если таковые у вас имеются. После этого вы можете запускать bckp как системную команду.

Как автоматизировать операции резервного копирования «по расписанию»

Несколько слов об автоматизации резервного копирования. Для этой цели служит системный планировщик cron, который считывает рабочие инструкции из специального crontab-файла. Чтобы определить такие инструкции, необходимо создать и отредактировать свой crontab-файл при помощи команды:

crontab -e

Инструкции записываются в строго определённом формате (поля разделяются пробелами):

минуты часы день_месяца месяц день_недели команда

Один из вариантов расписания операций резервного копирования может выглядеть следующим образом:

30 23 10,20,30 * * /usr/local/bin/bckp

Это означает, что сценарий резервного копирования (следует указать полный путь к этому файлу) будет выполняться в 23:30 10-го, 20-го и 30-го числа каждого месяца независимо от дня недели. (Звёздочки обозначают весь допустимый диапазон значений, в данном случае: каждый месяц – в 4-м поле, любой день недели – в 5-м поле)

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

0 5 * * 3,5 /usr/local/bin/bckp

Здесь резервные копии будут создаваться в 5:00 по средам и пятницам в каждом месяце (звёздочка в 4-м поле), независимо от числа (звёздочка в 3-м поле).

Обо всех тонкостях составления расписания можно прочитать в руководстве man 5 crontab.

Итоги и выводы

Рассмотренный в данной статье сценарий резервного копирования обладает скромными функциональными свойствами. Но не в этом состояла его главная задача, а в том, чтобы читатель понял, что можно делать в командной строке, и не только скопировал и выполнил предложенный командный файл, а заинтересовался расширением его функций, занялся исследованием необъятных возможностей, предоставляемых командными оболочками. И если кто-то, прочитав эту статью, попробует усовершенствовать приведённый здесь код, или напишет собственный вариант, или реализует свою независимую идею, то я сочту, что основная цель достигнута.

Ресурсы для скачивания

static.content.url=http://www.сайт/developerworks/js/artrating/

ArticleID=458335

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

for VAR in 1 2 3...N do done или в одну строку: for VAR in 1 2 3...N; do ; done
В переменную допускается подставлять как числовые значения, так и ASCII символы.
Пример: $ for i in 1 2 A B Abc ; do echo $i; done 1 2 A B Abc Пример перечисления в переменную файлов по "маске" для перекодирования видео: for i in *.avi; do ; done

2. Подстановка результатов выполнения другой команды

for VAR in $(); do ; done
Пример использования результатов команды seq: for i in $(seq [КЛЮЧ]); do echo $i; done $ for i in $(seq 3); do echo $i; done 1 2 3 $ for i in $(seq 3 5); do echo $i; done 3 4 5 $ for i in $(seq 2 2 6); do echo $i; done 2 4 6 Пример использования результатов команды ls: $ for i in $(ls /$HOME/Video); do echo $i; done 001.avi 002.avi 003.avi

3. Подстановка используя Си-стиль (C-style)

for ((EXPR1; EXPR2; EXPR3)) do <список команд> done for ((i=1; i<=3 ; i++)); do echo $i; done $ for ((i=1; i<=3 ; i++)); do echo $i; done 1 2 3 Подробнее о применении C-style в Bash

4. Перечисление при помощи фигурных скобок {..}

Применение синтаксиса {START..END} поддерживается начиная с bash version 3.0+ , а начиная с bash version 4.0+ поддерживается синтаксис {START..END..INCREMENT} :

for VAR in {..} do done или for VAR in {....} do done Примеры: $ for i in {1..3}; do echo $i; done 1 2 3 или $ for i in {4..8..2}; do echo $i; done 4 6 8 Отсчёт возможен как на приращение, так и на уменьшение значений: $ for i in {6..-4..3}; do echo $i; done 6 3 0 -3

5. Подстановка параметров (in "$@")

Выполняет команды для каждого параметра, который был передан сценарию. for VAR in $@ do done или в одну строку: for VAR in $@; do ; done
Так если создать скрипт test.sh #!/bin/sh for VAR in $@ do echo $VAR done то при запуске его с параметрами: $ ./test.sh param1 param2 param3 param1 param2 param3 Часть in $@ можно опускать. Тогда скрипт test.sh перепишется: #!/bin/sh for VAR do echo $VAR done
Приведу пару примеров (с in и без): $ function FUNC_1 { for VAR in $@; do echo $VAR; done; } $ FUNC_1 param1 param2 param3 param1 param2 param3 $ function FUNC_2 { for VAR; do echo $VAR; done; } $ FUNC_2 param1 param2 param3 param1 param2 param3

6. Применение continue и break в цикле for

Для всех вышеперечисленных конструкций возможно использовать команды "continue" для перехода к следующему элементу цикла или "break" для выхода из цикла.

Пример (завершить при i=6 и не выполнять при i=3 и i=5): for i in {1..8}; do if [ $i -eq 6 ]; then break; fi if [ $i -eq 3 ] || [ $i -eq 5 ]; then continue; fi echo $i done Результат выполнения: $ $ for i in {1..8}; do \ > if [ $i -eq 6 ]; then break; fi; \ > if [ $i -eq 3 ] || [ $i -eq 5 ]; then continue; fi; \ > echo $i; \ > done 1 2 4

  • 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.