Сайт о телевидении

Сайт о телевидении

» » Командный язык shell. Функция вычисления факториала fact

Командный язык shell. Функция вычисления факториала fact

Командный язык shell (в переводе - раковина, скорлупа) фактически есть язык программирования очень высокого уровня. На этом языке пользователь осуществляет управление компьютером. Обычно, после входа в систему вы начинаете взаимодействовать с командной оболочкой. Признаком того, что оболочка (shell) готова к приему команд служит выдаваемый ею на экран промптер. В простейшем случае это один доллар ("$"). Shell не является необходимым и единственным командным языком (хотя именно он стандартизован в рамках POSIX - стандарта мобильных систем). Например, немалой популярностью пользуется язык cshell, есть также kshell, bashell и другие. Более того, каждый пользователь может создать свой командный язык. Может одновременно на одном экземпляре операционной системы работать с разными командными языками. shell - это одна из многих команд UNIX. То есть в набор команд оболочки "shell" входит команда "sh" - вызов интерпретатора "shell". Первый "shell" вызывается автоматически при вашем входе в систему и выдает на экран промтер. После этого вы можете вызывать на выполнение любые команды, в том числе и снова сам "shell", который вам создаст новую оболочку внутри прежней. Так например, если вы подготовите в редакторе файл "file_1":

Echo Hello!

то это будет обычный текстовый файл, содержащий команду "echo", которая при выполнении выдает все написанное правее ее на экран. Можно сделать файл "file_1" выполняемым с помощью команды "chmod 755 file_1". Но его можно выполнить, вызвав явно команду "sh" ("shell"):

Sh file_1

Sh < file1

Файл можно выполнить и в текущем экземпляре "shell". Для этого существует специфическая команда "." (точка), т.е.

File_1

Поскольку UNIX - система многопользовательская, вы можете даже на персональном компьютере работать параллельно, скажем, на 12-ти экранах (переход с экрана на экран ALT/функциональная клавиша), имея на каждом экране нового (или одного и того же) пользователя со своей командной оболочкой. Можете и в графическом режиме X-Window также открыть большое число окон, а в каждом окне может быть свой пользователь со своей командной оболочкой... Стержневым элементом языка shell является команда.

Структуры команд:

Команды в shell обычно имеют следующий формат:

<имя команды> <флаги> <аргумент(ы)>

Например:

Ls -ls /usr/bin

Где ls - имя команды выдачи содержимого директория, -ls - флаги ("-" - признак флагов, l - длинный формат, s - об"ем файлов в блоках), /usr/bin - директорий, для которого выполняется команда. Эта команда выдаст на экран в длинном формате содержимое директория /usr/bin, при этом добавит информацию о размере каждого файла в блоках. К сожалению, такая структура команды выдерживается далеко не всегда. Не всегда перед флагами ставится минус, не всегда флаги идут одним словом. Есть разнообразие и в представлении аргументов. К числу команд, имеющих экзотические форматы, относятся и такие "ходовые" команды, как сс - вызов компилятора языка С, tar - работа с архивами, dd - копирование файла с преобразованием, find - поиск файлов и ряд других. Как правило, первое слово shell воспринимает, как команду. Поэтому в командной строке

первое слово будет расшифровано shell, как команда (конкатенации), которая выдаст на экран файл с именем "cat" (второе слово), находящийся в текущем директории. Перенаправление команд Стандартный ввод (вход) - "stdin" в ОС UNIX осуществляется с клавиатуры терминала, а стандартный вывод (выход) - "stdout" направлен на экран терминала. Существует еще и стандартный файл диагностических сообщений - "stderr", о котором речь будет чуть позже. Команда, которая может работать со стандартным входом и выходом, называется ФИЛЬТРОМ. Пользователь имеет удобные средства перенаправления ввода и вывода на другие файлы (устройства). Символы ">" и ">>" обозначают перенаправление вывода. ls >file_1 команда "ls" сформирует список файлов текущего каталога и поместит его в файл "file_1" (вместо выдачи на экран). Если файл "file_1" до этого существовал, то он будет затерт новым.

Pwd >>file_1

команда pwd сформирует полное имя текущего каталога и поместит его в конец файла "file_1", т.е. ">>" добавляет в файл, если он непустой. Символы "<" и "<<" обозначают перенаправление ввода.

Wc -l

подсчитает и выдаст на экран число строк в файле file_1.

Ed file_2 <

создаст с использованием редактора файл "file_2", непосредственно с терминала. Окончание ввода определяется по символу, стоящему правее "<<" (т. е. "!"). То есть ввод будет закончен, когда первым в очередной строке будет "!". Можно сочетать перенаправления. Так

Wc -l file_4

Wc -l >file_4

выполняются одинаково: подсчитывается число строк файла "file_3" и результат помещается в файл "file_4". Средство, объединяющее стандартный выход одной команды со стандартным входом другой, называется КОНВЕЙЕРОМ и обозначается вертикальной чертой "|".

Ls | wc -l

список файлов текущего каталога будет направлен на вход команды "wc", которая на экран выведет число строк каталога. Конвейером можно объединять и более двух команд, когда все они, возможно кроме первой и последней - фильтры:

Cat file_1 | grep -h result | sort | cat -b > file_2

Данный конвейер из файла "file_1" ("cat") выберет все строки, содержащие слово "result" ("grep"), отсортирует ("sort") полученные строки, а затем пронумерует ("cat -b") и выведет результат в файл "file_2". Поскольку устройства в ОС UNIX представлены специальными файлами, их можно использовать при перенаправлениях. Специальные файлы находятся в каталоге "/dev". Например, "lp" - печать; "console" - консоль; "ttyi" - i-ый терминал; "null" - фиктивный (пустой) файл (устройство). Тогда, например,

Ls > /dev/lp

выведет содержимое текущего каталога на печать, а file_1 < /dev/null обнулит файл "file_1".

Sort file_1 | tee /dev/lp | tail -20

В этом случае будет отсортирован файл "file_1" и передан на печать, а 20 последних строк также будут выданы на экран. Вернемся к перенаправлению выхода. Стандартные файлы имеют номера:

0 - stdin, 1 - stdout 2 - stderr. Если вам не желательно иметь на экране сообщение об ошибке, вы можете перенаправить его с экрана в указанный вами файл (или вообще "выбросить", перенаправив в файл "пустого устройства" - /dev/null). Например при выполнении команды

Cat file_1 file_2

которая должна выдать на экран последовательно содержимое файлов "file_1" и "file_2", выдаст вам, например, следующее

111111 222222 cat: f2: No such file or directory

где 111111 222222 - содержимое файла "file_1", а файл "file_2" отсутствует, о чем команда "cat" выдала сообщение в стандартный файл диагностики, по умолчанию, как и стандартный выход, представленный экраном. Если вам не желательно такое сообщение на экране, его можно перенаправить в указанный вами файл:

Cat file_1 file_2 2>f-err

сообщения об ошибках будут направляться (об этом говорит перенаправление "2>") в файл "f-err". Кстати, вы можете всю информацию направлять в один файл "ff", использовав в данном случае конструкцию

Cat file_1 file_2 >>ff 2>ff

Можно указать не только какой из стандартных файлов перенаправлять, но и в какой стандартный файл осуществить перенаправление.

Cat file_1 file_2 2>>ff 1>&2

Здесь сначала "stderr" перенаправляется (в режиме добавления) в файл "ff", а затем стандартный выход перенаправляется на "stderr", которым к этому моменту является файл "ff". То есть результат будет аналогичен предыдущему. Конструкция "1>&2" - означает, что кроме номера стандартного файла, в который перенаправить, необходимо впереди ставить "&"; вся конструкция пишется без пробелов. <- закрывает стандартный ввод. >- закрывает стандартный вывод. Командные файлы. Для того, чтобы текстовый файл можно было использовать как команду, существует несколько возможностей. Пусть с помощью редактора создан файл с именем "cmd", содержащий одну строку следующего вида:

Date; pwd; ls

Можно вызвать shell как команду, обозначаемую "sh", и передать ей файл "cmd", как аргумент или как перенаправленный вход, т.е.

$ sh cmd

$ sh

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

Chmod 711 cmd

сделает код защиты "rwx__x__x". Тогда простой вызов

приведет к выполнению тех же трех команд. Результат будет тот же, если файл с содержимым

Date; pwd; ls

представлен в виде: date pwd ls так как переход на другую строку также является разделителем в последовательности команд. Таким образом, выполняемыми файлами могут быть не только файлы, полученные в результате компиляции и сборки, но и файлы, написанные на языке shell. Их выполнение происходит в режиме интерпретации с помощью shell-интерпретатора

Отладка командных файлов

В SHELL используются два механизма отладки командных файлов. Первый из них: set -v выводит строки командного файла по мере их чтения. Этот режим применяется при поиске синтаксических ошибок. Для его использования не требуется производить модификацию командного файла, например: sh -v proc... здесь proc - имя командного файла. Ключ -v может использоваться вместе с ключом -n, предотвращающим выполнение следующих за ним команд (команда set -n блокирует терминал до тех пор, пока не вводится признак конца файла EOF). Команда set -х выводит команды по мере их выполнения, причём на терминал выводятся строки программы и на место переменных подставляются их значения. Для отмены ключей -x и -v можно воспользоваться командой set - а для установки - присвоить соответствующее значение макропеременной. СРЕДА SHELL (ПЕРЕМЕННЫЕ И ПАРАМЕТРЫ) На языке shell можно писать командные файлы и с помощью команды "chmod" делать их выполняемыми. После этого они ни чем не отличаются от прочих команд ОС UNIX.

Shell-переменные

Имя shell-переменной - это начинающаяся с буквы последовательность букв, цифр и подчеркиваний. Значение shell-переменной - строка символов. То, что в shell всего два типа данных: строка символов и текстовый файл, с одной стороны, позволяет легко вовлекать в программирование конечных пользователей, никогда ранее программированием не занимавшихся, а с другой стороны, вызывает некий внутренний протест у многих программистов, привыкших к существенно большему разнообразию и большей гибкости языковых средств. Однако интересно наблюдать то, как высококлассные программисты, освоившись с "правилами игры" shell, пишут на нем программы во много раз быстрее, чем на Си, но, что особенно интересно, в ряде случаев эти программы работают даже быстрее, чем реализованные на Си. Имя переменной аналогично традиционному представлению об идентификаторе, т.е. именем может быть последовательность букв, цифр и подчеркиваний, начинающаяся с буквы или подчеркивания. Для присваивания значений переменным может использоваться оператор присваивания "=".

Var_1=13 - "13" - это не число, а строка из двух цифр. var_2="ОС UNIX" - здесь двойные кавычки (" ") необходимы, так как в строке есть пробел.

Возможны и иные способы присваивания значений shell-переменным. Так например запись,

DAT=`date`

приводит к тому, что сначала выполняется команда "date" (обратные кавычки говорят о том, что сначала должна быть выполнена заключенная в них команда), а результат ее выполнения, вместо выдачи на стандартный выход, приписывается в качестве значения переменной, в данном случае "DAT". Можно присвоить значение переменной и с помощью команды "read", которая обеспечивает прием значения переменной с (клавиатуры) дисплея в диалоговом режиме. Обычно команде "read" в командном файле предшествует команда "echo", которая позволяет предварительно выдать какое-то сообщение на экран. Например:

Echo -n "Введите трехзначное число:" read x

При выполнении этого фрагмента командного файла, после вывода на экран сообщения

Введите трехзначное число:

интерпретатор остановится и будет ждать ввода значения с клавиатуры. Если вы ввели, скажем, "753" то это и станет значением переменной "x". Одна команда "read" может прочитать (присвоить) значения сразу для нескольких переменных. Если переменных в "read" больше, чем их введено (через пробелы), оставшимся присваивается пустая строка. Если передаваемых значений больше, чем переменных в команде "read", то лишние игнорируются. При обращении к shell-переменной необходимо перед именем ставить символ "$". Так команды echo $var_2 echo var_2 выдадут на экран

ОС UNIX var_2 Экранирование

Рассмотрим более подробно приемы экранирования, используемые в shell. В качестве средств экранирования используются двойные кавычки (" "), одинарные кавычки (" ") и бэк-слэш (\). Из примеров очевидно их действие: Можно в одной строке записывать несколько приcваиваний.

X=22 y=33 z=$x A="$x" B="$x" C=\$x D="$x + $y + $z" E="$x + $y + $z" F=$x\ +\ $y\ +\ $z

(присваивание G=$x+$y не было бы выполнено из-за пробелов) Тогда

Echo A = $A B = $B C = $C echo D = $D E = $E F = $F eval echo evaluated A = $A eval echo evaluated B = $B eval echo evaluated C = $C

Выдадут на экран

A = 22 B = $x C = $x D = 22 + 33 + 22 E = $x + $y + $z F = 22 + 33 + 22 evaluated A = 22 evaluated B = 22 evaluated C = 22

Приведем еще примеры, связанные с экранированием перевода строки. Пусть переменной "string" присвоено значение "массива" 2x3: abc def Обратим внимание, что для избежания присваивания лишних пробелов вторая строка массива начата с первой позиции следующей строки: string="abc def" Тогда три варианта записи переменной в команде "echo" echo $string echo "$string" echo "$string" дадут соответственно три различных результата: abc def $string abc def а последовательность команд echo "str_1 str_2" > file_1 echo "str_1 str_2" > file_2 cat file_1 file_2 даст выдаст последовательно одинаковые файлы file_1 и file_2: str_1 str_2 str_1 str_2 Заметим также, что бэк-слэш (\) не только экранирует следующий за ним символ, что позволяет использовать специальные символы просто как символы, представляющие сами себя (он может экранировать и сам себя - \\), но в командном файле бэк-слэш позволяет об"единять строки в одну (экранировать конец строки). Например, приводившийся ранее пример командной строки:

Cat file_1 | grep -h result | sort | cat -b > file_2

может быть записан в командном файле, скажем, как

Cat file_1 | grep -h \ result | sort | cat -b > file_2

Кстати, эффект продолжения командной строки обеспечивает и символ конвейера. В данном случае это может дать более симпатичный результат, например:

Cat file_1 | grep -h result | sort | cat -b > file_2

Манипуляции с shell-переменными Несмотря на то, что shell-переменные в общем случае воспринимаются как строки, т. е. "35" - это не число, а строка из двух символов "3" и "5", в раде случаев они могут интерпретироваться иначе, например, как целые числа. Разнообразные возможности имеет команда "expr". Проиллюстрируем некоторые на примерах: Выполнение командного файла:

X=7 y=2 a=`expr $x + $y` ; echo a=$a a=`expr $a + 1` ; echo a=$a b=`expr $y - $x` ; echo b=$b c=`expr $x "*" $y` ; echo c=$c d=`expr $x / $y` ; echo d=$d e=`expr $x % $y` ; echo e=$e

выдаст на экран

A=9 a=10 b=-5 c=14 d=3 e=1

Операция умножения ("*") обязательно должна быть заэкранирована, поскольку в shell этот значок воспринимается, как спецсимвол, означающий, что на это место может быть подставлена любая последовательность символов. С командой "expr" возможны не только (целочисленные) арифметические операции, но и строковые:

A=`expr "cocktail" : "cock"` ; echo $A B=`expr "cocktail" : "tail"` ; echo $B C=`expr "cocktail" : "cook"` ; echo $C D=`expr "cock" : "cocktail"` ; echo $D

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

4 0 0 0

Экспорт переменных В ОС UNIX существует понятие процесса. Процесс возникает тогда, когда запускается на выполнение какая-либо команда. Например, при наборе на клавиатуре "р " порождается процесс "р". В свою очередь "р" может породить другие процессы. Допустим, что "р" вызывает "р1" и "р2", которые последовательно порождают соответствующие процессы. У каждого процесса есть своя среда - множество доступных ему переменных. Например, до запуска "р" уже существовала среда, в которой уже были определены некоторые переменные. Запуск "р" порождает новую среду; уже в ней будут порождены "р1" и "р2". Переменные локальны в рамках процесса, в котором они объявлены, т.е. где им присвоены значения. Для того, чтобы они были доступны и другим порождаемым процессам, надо передать их явным образом. Для этого используется встроенная команда "export".

Параметры

В командный файл могут быть переданы параметры. В shell используются позиционные параметры (т.е. существенна очередность их следования). В командном файле соответствующие параметрам переменные (аналогично shell-переменным) начинаются с символа "$", а далее следует одна из цифр от 0 до 9: Пусть "examp-1" вызывается с параметрами "cock" и "tail". Эти параметры попадают в новую среду под стандартными именами "1" и "2". В (стандартной) переменной с именем "0" будет храниться имя вызванного расчета. При обращении к параметрам перед цифрой ставится символ доллара "$" (как и при обращении к переменным): $0 соответствует имени данного командного файла; $1 первый по порядку параметр; $2 второй параметр и т.д. Поскольку число переменных, в которые могут передаваться параметры, ограничено одной цифрой, т.е. 9-ю ("0", как уже отмечалось имеет особый смысл), то для передачи большего числа параметров используется специальная команда "shift". Своеобразный подход к параметрам дает команда "set". Например, фрагмент

Set a b с echo первый=$1 второй=$2 третий=$3

выдаст на экран

Первый=a второй=b третий=c

т.е. команда "set" устанавливает значения параметров. Это бывает очень удобно. Например, команда "date" выдает на экран текущую дату, скажем, "Mon May 01 12:15:10 2000", состоящую из пяти слов, тогда

Set `date` echo $1 $3 $5

выдаст на экран

Mon 01 2000

Команда "set" позволяет также осуществлять контроль выполнения программы, например: set -v на терминал выводятся строки, читаемые shell. set +v отменяет предыдущий режим. set -x на терминал выводятся команды перед выполнением. set +x отменяет предыдущий режим. Команда "set" без параметров выводит на терминал состояние программной среды.

Подстановки shell-интерпретатора

Перед началом непосредственной интерпретации и выполнением команд, содержащихся в командных файлах, shell выполняет различные виды подстановок: 1. ПОДСТАНОВКА РЕЗУЛЬТАТОВ. Выполняются все команды, заключенные в обратные кавычки, и на их место подставляется результат. 2. ПОДСТАНОВКА ЗНАЧЕНИЙ ПАРАМЕТРОВ И ПЕРЕМЕННЫХ. То есть слова, начинающиеся на "$", заменяются соответствующими значениями переменных и параметров. 3. ИНТЕРПРЕТАЦИЯ ПРОБЕЛОВ. Заэкранированные пробелы игнорируются. 4. ГЕНЕРАЦИЯ ИМЕН ФАЙЛОВ. Проверяются слова на наличие в них спецсимволов ("*", "?","") и выполняются соответствующие генерации. Программная среда Каждый процесс имеет среду, в которой он выполняется. Shell использует ряд переменных этой среды. Если вы наберете команду "set" без параметров, то на экран будет выдана информация о ряде стандартных переменных, созданных при входе в систему (и передаваемых далее всем вашим новым процессам "по наследству"), а также переменных, созданных и экспортируемых вашими процессами. Конкретный вид и содержание выдаваемой информации в немалой степени зависит от того, какая версия UNIX используется и как инсталлирована система.

Результат выполнения команды set без параметров (не полный):

HOME=/root PATH=/usr/local/bin:/usr/bin:/bin:.:/usr/bin/X11: IFS= LOGNAME=sae MAIL=/var/spool/mail/sae PWD=/home/sae/STUDY/SHELL PS1=${PWD}:" " PS2=> SHELL=/bin/bash

Прокомментируем значения переменных. HOME=/root - это имя домашнего директория, в котором пользователь оказывается после входа в систему. То есть, правильно набрав имя и пароль, я окажусь в директории "/root". PATH=/bin:/usr/bin:.:/usr/local/bin:/usr/bin/X11 - эта переменная задает последовательность файлов, которые просматривает "shell" в поисках команды. Имена файлов разделяются здесь двоеточиями. Последовательность просмотра соответствует очередности следования имен в тропе. Но первоначально поиск происходит среди так называемых встроенных команд. В число встроенных команд входят наиболее часто используемые команды, например "echo", "cd", "pwd", "date". После этого система просматривает директорий "/bin", в котором могут находиться команды "sh", "cp", "mv", "ls" и т.п. Затем директорий "/usr/bin" с командами "cat", "сс", "expr", "nroff", "man" и многими другими. Далее поиск происходит в текущем директории (".", или другое обозначение "пусто", т.е.""), где скорее всего находятся написанные вами команды. После набора командной строки и нажатия "shell" (после выполнения необходимых подстановок) распознает имя, соответствующее команде и осуществляет ее поиск в директориях, перечисленных в PATH. Если команда размещена вне этих директориев, она не будет найдена. Если присутствует несколько команд с одинаковым именем, то вызвана будет та, которая расположена в директории, просматриваемом первым. PATH, как и прочие переменные, можно легко менять, добавляя, переставляя или исключая директории. IFS= (Внутренний Разделитель Полей) перечисляет символы, которые служат для разделения слов (полей). Таковыми являются "пробел", "табуляция" и "перевод строки", поэтому здесь слева от присваивания ничего не видно и занято две строки. LOGNAME=root - имя входа ("имя" пользователя). MAIL=/var/spool/mail/root - имя файла, в который поступает (электронная) почта. PWD=/root - имя текущего директория PS1=${PWD}:" " - вид промтера. В данном случае в промптере будет выдаваться имя текущего директория двоеточие и пробел. То есть здесь будет "/root: ". PS2=> - этот промтер (здесь ">") используется как приглашение к продолжению ввода (в очередной строке) незаконченной команды. Например, наберите открывающую скобку "(" и после нажатия в следующей строке вы увидите этот промптер. Если пока не знаете, что дальше делать, наберите закрывающую скобку ")" и он исчезнет. SHELL=/bin/sh - эта переменная указывает оболочку, которую использует пользователь. В данном случае используется стандартный shell ("sh"). Исходная среда устанавливается автоматически при входе в систему с использованием файлов типа "/etc/rc" и "/etc/.profile". Один из способов просто изменит среду (например, тропу поиска команд, вид промтера, вид оболочки, цвет экрана и т.п.) можно, разместив эту информацию в своем домашнем директории в специализированном файле ".profile" (${HOME}/.profile), присвоив нужные значения переменным среды. То есть вызвать это файл в редактор и написать, что пожелаете). Тогда при каждом вашем входе в систему этот файл будет автоматически выполняться и устанавливать новую среду. Этот файл должен ОБЯЗАТЕЛЬНО размещаться в вашем ДОМАШНЕМ директории (директории входа). Следует иметь в виду, что имена файлов, начинающиеся с точки, вообще имеют особый статус. Так, они не выдаются на экран простой командой "ls" - необходимо вызывать эту команду с флагом "-a". Кстати, и не уничтожаются огульно командой "rm *". Сам интерпретатор shell автоматически присваивает значения следующим переменным (параметрам): ? значение, возвращенное последней командой; $ номер процесса; ! номер фонового процесса;

  1. число позиционных параметров, передаваемых в shell;
  • перечень параметров, как одна строка;

@ перечень параметров, как совокупность слов; - флаги, передаваемые в shell. При обращении к этим переменным (т.е при использовании их в командном файле - shell-программе) следует впереди ставить "$". Важную роль при создании уникальных файлов играет специальная переменная "$$", значение которой соответствует номеру процесса, выполняющего данный расчет. Каждый новый расчет, выполняемый компьютером, инициирует один или несколько процессов, автоматически получающих номера по порядку. Поэтому, используя номер процесса в качестве имени файла, можно быть уверенным, что каждый новый файл будет иметь новое имя (не запишется на место уже существующего). Достоинство является и главным недостатком такого способа именования файлов. Неизвестно, какие имена будут присвоены файлам. И, если в рамках данного процесса можно найти файл "не глядя", т.е., обратившись к нему, используя $$, то потом такие файлы можно легко потерять. Это создает дополнительные проблемы при отладке программ. Вызов интерпритатора Вслед за регистрацией пользователя в системе (с помощью команды login) вызывается интерпретатор языка SHELL. Если регистрационный справочник пользователя содержит файл.profile, то прежде чем с терминала будет принята хотя бы одна команда, интерпретатор выполняет этот файл (подразумевается, что файл.profile содержит команды). При вызове могут указываться следующие ключи: -c строка Команды считываются из заданной строки. -s Команды читаются из стандартного файла ввода. Сообщения интерпретатора записываются в стандартный файл диагностик. -i Интерактивный режим работы. Если первым символом параметра "0" является знак -, то команды считываются из файла.profile.

ПРОГРАММНЫЕ СТРУКТУРЫ===

Как во всяком языке программирования в тексте на языке shell могут быть комментарии. Для этого используется символ "#". Все, что находится в строке (в командном файле) левее этого символа, воспринимается интерпретатором как комментарий. Например,

# Это комментарий.

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

Команда test ("")

Команда test проверяет выполнение некоторого условия. С использованием этой (встроенной) команды формируются операторы выбора и цикла языка shell. Два возможных формата команды:

Test условие

[ условие ]

мы будем пользоваться вторым вариантом, т.е. вместо того, чтобы писать перед условием слово "test", будем заключать условие в скобки, что более привычно для программистов. На самом деле shell будет распознавать эту команду по открывающей скобке "[", как слову, соответствующему команде "test". Между скобками и содержащимся в них условием обязательно должны быть пробелы. Пробелы должны быть и между значениями и символом сравнения или операции В shell используются условия различных "типов". УСЛОВИЯ ПРОВЕРКИ ФАЙЛОВ: -f file файл "file" является обычным файлом; -d file файл "file" - каталог; -с file файл "file" - специальный файл; -r file имеется разрешение на чтение файла "file"; -w file имеется разрешение на запись в файл "file"; -s file файл "file" не пустой.

УСЛОВИЯ ПРОВЕРКИ СТРОК: str1 = str2 строки "str1" и "str2" совпадают; str1 != str2 строки "str1" и "str2" не совпадают; -n str1 строка "str1" существует (непустая); -z str1 строка "str1" не существует (пустая). Примеры.

X="who is who"; export x; [ "who is who" = "$x" ]; echo $? 0 x=abc ; export x ; [ abc = "$x" ] ; echo $? 0 x=abc ; export x ; [ -n "$x" ] ; echo $? 0 x="" ; export x ; [ -n "$x" ] ; echo $? 1

Кроме того, существуют два стандартных значения условия, которые могут использоваться вместо условия (для этого не нужны скобки). УСЛОВИЯ СРАВНЕНИЯ ЦЕЛЫХ ЧИСЕЛ: x -eq y "x" равно "y", x -ne y "x" неравно "y", x -gt y "x" больше "y", x -ge y "x" больше или равно "y", x -lt y "x" меньше "y", x -le y "x" меньше или равно "y". СЛОЖНЫЕ УСЛОВИЯ: Реализуются с помощью типовых логических операций: ! (not) инвертирует значение кода завершения. -o (or) соответствует логическому "ИЛИ". -a (and) соответствует логическому "И".

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

В общем случае оператор "if" имеет структуру

If условие then список

Здесь "elif" сокращенный вариант от "else if" может быть использован наряду с полным, т.е. допускается вложение произвольного числа операторов "if" (как и других операторов). Разумеется "список" в каждом случае должен быть осмысленный и допустимый в данном контексте. Самая усеченная структура этого оператора

If условие then список fi

если выполнено условие (как правило это ком получен код завершения "0", то выполняется "список", иначе он пропускается. Примеры. Пусть написан "if-1"

If [ $1 -gt $2 ]

then pwd else echo $0: Hello!

Тогда вызов if-1 12 11 даст /home/sae/STUDY/SHELL а if-1 12 13 даст if-1: Hello!

Оператор вызова ("case")

Оператор выбора "case" имеет структуру:

Case строка in

шаблон) список команд;; шаблон) список команд;; ... шаблон) список команд;;

Здесь "case" "in" и "esac" - служебные слова. "Строка" (это может быть и один символ) сравнивается с "шаблоном". Затем выполняется "список команд" выбранной строки. Непривычно выглядят в конце строк выбора ";;", но написать здесь ";" было бы ошибкой. Для каждой альтернативы может быть выполнено несколько команд. Если эти команды будут записаны в одну строку, то символ ";" будет использоваться как разделитель команд. Обычно последняя строка выбора имеет шаблон "*", что в структуре "case" означает "любое значение". Эта строка выбирается, если не произошло совпадение значения переменной (здесь $z) ни с одним из ранее записанных шаблонов, ограниченных скобкой ")". Значения просматриваются в порядке записи.

Оператор цикла с перечислением ("for")

Оператор цикла "for" имеет структуру:

For имя

do список команд done где "for" - служебное слово определяющее тип цикла, "do" и "done" - служебные слова, выделяющие тело цикла. Пусть команда "lsort" представлена командным файлом

For i in file_1 file_2 file_3 do proc_sort $i done

В этом примере имя "i" играет роль параметра цикла. Это имя можно рассматривать как shell-переменную, которой последовательно присваиваются перечисленные значения (i=file_1, i=file_2, i=file_3), и выполняется в цикле команда "proc_sort". Часто используется форма "for i in *", означающая "для всех файлов текущего каталога". Пусть "proc_sort" в свою очередь представляется командным файлом

Cat $1 | sort | tee /dev/lp > ${1}_sorted

т.е. последовательно сортируются указанные файлы, результаты сортировки выводятся на печать ("/dev/lp") и направляются в файлы file_1_sorted file_2_sorted и file_3_sorted

Оператор цикла с истинным условием ("while")

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

While условие

do список команд done где "while" - служебное слово определяющее тип цикла с истинным условием. Список команд в теле цикла (между "do" и "done") повторяется до тех пор, пока сохраняется истинность условия (т.е. код завершения последней команды в теле цикла равен "0") или цикл не будет прерван изнутри специальными командами ("break", "continue" или "exit"). При первом входе в цикл условие должно выполняться. Команда "break [n]" позволяет выходить из цикла. Если "n" отсутствует, то это эквивалентно "break 1". "n" указывает число вложенных циклов, из которых надо выйти, например, "break 3" - выход из трех вложенных циклов. В отличие от команды "break" команда "continue [n]" лишь прекращает выполнение текущего цикла и возвращает на НАЧАЛО цикла. Она также может быть с параметром. Например, "continue 2" означает выход на начало второго (если считать из глубины) вложенного цикла. Команда "exit [n]" позволяет выйти вообще из процедуры с кодом возврата "0" или "n" (если параметр "n" указан). Эта команда может использоваться не только в циклах. Даже в линейной последовательности команд она может быть полезна при отладке, чтобы прекратит выполнение (текущего) расчета в заданной точке.

Оператор цикла с ложным условием ("until")

Оператор цикла "until" имеет структуру:

Until условие

do список команд done где "until" - служебное слово определяющее тип цикла с ложным условием. Список команд в теле цикла (между "do" и "done") повторяется до тех пор, пока сохраняется ложность условия или цикл не будет прерван изнутри специальными командами ("break", "continue" или "exit"). При первом входе в цикл условие не должно выполняться. Отличие от оператора "while" состоит в том, что условие цикла проверяется на ложность (на ненулевой код завершения последней команды тела цикла) проверяется ПОСЛЕ каждого (в том числе и первого!) выполнения команд тела цикла. Пример.

Until false do

read x if [ $x = 5 ] then echo enough ; break else echo some more fi

Здесь программа с бесконечным циклом ждет ввода слов (повторяя на экране фразу "some more"), пока не будет введено "5". После этого выдается "enough" и команда "break" прекращает выполнение цикла.

Пустой оператор

Пустой оператор имеет формат

:

Ничего не делает. Возвращает значение "0".".

Функции в shell

Функция позволяет подготовить список команд shell для последующего выполнения. Описание функции имеет вид:

Имя() { список команд }

после чего обращение к функции происходит по имени. При выполнении функции не создается нового процесса. Она выполняется в среде соответствующего процесса. Аргументы функции становятся ее позиционными параметрами; имя функции - ее нулевой параметр. Прервать выполнение функции можно оператором "return [n]", где (необязательное) "n" - код возврата.

Обработка прерываний ("trap")

Бывает необходимо защитить выполнение программы от прерывания. Наиболее часто приходится встречаться со следующими прерываниями, соответствующими сигналам: 0 выход из интерпретатора, 1 отбой (отключение удаленного абонента), 2 прерывание от , 9 уничтожение (не перехватывается), 15 окончание выполнения. Для защиты от прерываний существует команда "trap", имеющая формат:

Trap "список команд" сигналы

Если в системе возникнут прерывания, чьи сигналы перечислены через пробел в "сигналы", то будет выполнен "список команд", после чего (если в списке команд не была выполнена команда "exit") управление вернется в точку прерывания и продолжится выполнение командного файла. Например, если перед прекращением по прерываниям выполнения какого то командного файла необходимо удалить файлы в "/tmp", то это может быть выполнено командой "trap":

Trap "rm /tmp/* ; exit 1" 1 2 15

которая предшествует прочим командам файла. Здесь, после удаления файлов будет осуществлен выход "exit" из командного файла.

МОСКВА 2007

Лабораторный практикум по курсу: “Операционная система Red Hat Linux

Лабораторная работа №5

Программирование в SHELL

Цель работы : на основе ранее полученных навыков работы с командным интерпретатором BASH овладеть начальными навыками программирования в командном интерпретаторе BASH для создания файлов сценария – SHELL скриптов.

Теоретические сведения

Выполнение отдельных команд в командном интерпретаторе не всегда является эффективным средством работы с SHELL оболочкой. Довольно часто требуется выполнять одни и те же последовательности действий при работе с операционной системой Linux каждый месяц, каждую неделю, каждый день и иногда и несколько раз в день. Например, предположим, вы работаете на фирме тестером программного обеспечения (Test Manager). Каждый день вам требуется в командной оболочке Linux выполнять один тот набор действий, а именно: монтирование устройства CD-ROM; копирование всей информации с него в папку, скажем, /opt/program на жестком диске; демонтирование CD-ROM; чтение файла readme из этой папки; установка программы с жесткого диска из папки /opt/program; создания нового файла отчета в директории /home/user/report_program удаление всего содержимого дистрибутива с жесткого диска. И это притом, что основная ваша работа заключается в тестировании программного обеспечения на предмет выявления недоработок (bags). Задача на первый взгляд простая, но выполнять подобные действия по несколько раз в день, притом, что вы занимаетесь и другими делами, неудобно. Или, например, вы работаете в фирме системным администратором и обслуживаете около 50 компьютеров. Каждый день вам требуется раскопировать на все компьютеры один и тот же файл с инструкциями на текущий день директора фирмы для всех подчиненных. Кроме того, в ваши задачи также входит в конце рабочего дня сохранить все данные каждого пользователя на общем сервере фирмы с персональных компьютеров подчиненных. Задача также на первый взгляд простая, но работа по набору команд в командном интерпретаторе для 50 компьютеров займет много времени. А если к тому же вы при наборе будите ошибаться, например, от усталости? Данные для директора и их владельца очень важны, и потерять их Вам никак нельзя. Но это еще простые действия. А представьте, что названия файлов отчетов, название папок с данными, имена компьютеров каждый раз будут меняться? Что тогда делать? Выход один – написать файл-сценария , т.е. создать текстовый файл, в котором требуется описать всю последовательность команд с путями, опциями, аргументами, выражениями, действиями и так далее для выполнения определенного сценария. Например, резервирования данных. После ввода команд в текстовый файл-сценария, данный файл делают исполняемым или выполняют его специальных операторов выполнения командного интерпретатора. Использование файлов сценария является особенно эффективным, если вам очень часто требуется выполнять последовательность из большого числа команд. Также является эффективным средством, если в зависимости от результатов выполнения предыдущих команд зависит выполнение следующих. С помощью файлов-сценариев можно также использовать арифметические и логические выражения, создавать циклы, обеспечивающие многократное выполнение группы команд Linux.

Создание файлов-сценария. Создания сценариев представляет собой последовательный набор команд, операторов командного интерпретатора и некоторых других символов в текстовом файле. Файлы-сценария также называют скриптами, а командные интерпретаторы латинским названием SHELL. В связи с тем, что на сегодняшний день существуют разные командные интерпретаторы, написание файла-сценария с использованием дополнительных символов, например, метасимволов, совместно с командами не должно противоречить правилам синтаксиса выбранного командного интерпретатора, в котором предполагается выполнять данный скрипт. Процесс написания таких файлов называют SHELL программированием. Рассмотрим процесс программирования на примере интерпретатора BASH. Таким образом, скрипты и правила написания скриптов в данной лабораторной работе могут быть не переносимы на другие интерпретаторы, например, такие как С Shell или TC Shell.

Последовательность #!. Написание любого скрипта начинается с принадлежности его к одному из командных интерпретаторов. В первой строке скрипта обычно пишут последовательность #! , которая указывает системе, какой интерпретатор должен выполнить команды из данного сценария. Если первый символ пробел, считается, что сценарий написан для BASH или PDKSH. Если сценарий начинается только с символа # , то для его выполнения необходим TC Shell. Если же за символом # следует символ!, ядро запустит интерпретатор, путь которого указан далее в этой строке. Например, для BASH будет следующая запись: #!/bin/sh . Пробела или одиночного символа # в начале сценария достаточно для интерпретаторов BASH Shell и TC Shell только при условии, что они будут считывать свой сценарий. Чтобы один интерпретатор распознал сценарии другого, необходимо включать в сценарий символы #! , после чего указывается путь интерпретирующей программы. Тогда при вызове сценария будет прекращена работа текущего интерпретатора, вместо него будет загружен сценарий другого типа, а затем выполнен сценарий. Рекомендуется всегда начинать все сценарии с последовательности #! . Также строки, начинающиеся с символа # можно использовать для комментариев действий пользователя в скрипте. Встретив символ #, интерпретатор shell игнорирует эту строку. Каждый комментарий должен завершаться символом окончания строки. Использование комментариев является признаком хорошего тона.

Расширение и запуск скриптов. Обычно файлы-сценариев имеют не только имена, но также и расширения. Чаще всего в качестве расширений используют комбинацию букв sh от латинского слова shell. По такому расширению, не открывая файл, сразу понятно, что это shell скрипт, так как нам опять же понятно, что файл с расширением . c скорее всего является входным файлом языков высокого уровня С и С++. После набора содержимого файла, файл сохраняется. Запустить файл можно двумя способами либо сделать его исполняемым с помощью команды chmod, либо запускать его с помощью специальных операторов командного интерпретатора: sh и. Обычно пользователи задают для файл-сценария восьмеричные значения 750 или 550. В следующем примере сделаем скрипт script.sh выполняемым с помощью команды chmod и запустим его на выполнение из текущей директории в фоновом режиме.

chmod u+x script.sh

./ script.sh &

Теперь вернем файлу предыдущие атрибуты и запустим его на выполнение с помощью оператора BASH.

chmod u-x script.sh

sh script.sh&

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

echo “Добрый день!”

Команда read . При создании скриптов вы можете также применять переменные и сценарии, написанные для других интерпретаторов и команд. Команда read служит командой запроса пользователя на ввод информации. Она сочетается с подсказкой пользователю, которая приглашает ввести нужную информацию. Приведем пример использования read.

echo “Введите ваше имя: ”

read your _ name

echo “ Добрый день ,” your_name “!”

Данный скрипт усложнен по отношению к предыдущему. Здесь используется переменная your_name , значение которой потом применяется совместно с текстом.

Использование переменных. Как и языках программирования, в shell Вы также можете использовать переменные, присваивать значения переменным можно через оператор присваивания равно «=». Сначала вводится имя переменной, потом без пробела знак «=», затем без пробела значение переменной. Имя переменной может состоять из любого количества буквенных символов, включая символ подчеркивания. Имя может содержать и цифры, однако не должно начинаться с цифры. Все остальные символы (в частности, восклицательный знак, амперсанд и пробел) в имя входить не должны. Такие символы зарезервированы интерпретатором для специальных целей. Как следствие, имя может состоять только из одного слова, поскольку при синтаксическом анализе команд интерпретатор рассматривает пробел как разделитель имен команд и аргументов. Значение переменной может состоять из любой последовательности символов. В следующем примере присвоим переменной «ppp» значение «www123yyy».

ppp =” www 123 yyy

Если вы используете в качестве значения переменной строковое значение, используйте двойные кавычки. После присвоения значения вы можете пользоваться именем переменной для ссылки на это значение, например использовать его в качестве аргумента команд сценария. На значение переменной можно ссылаться посредством ее имени, которое предваряется оператором $. Знак доллара - это специальный оператор, который использует имя переменной для ссылки на ее значение, то есть фактически для ее вычисления. Теперь с помощью уже знакомой команды echo и переменной «ppp» можно отображать значение этой переменной.

echo $ppp

www123yyy

Аргументы командной строки. В сценарии, как и в командах Linux, можно использовать аргументы. Аргумент в сценарии обозначается оператором $ с указанием номера позиции в командной строке. Нумерация параметров начинается с единицы, а заканчивается девятью. Первый параметр задается переменной $1, второй - $2 и т. д. Аргумент $0 резерви­руется для имени shell-сценария, в качестве которого выступает первое слово, находя­щееся в командной строке. По умолчанию имеется возможность устанавливать 9 переменных с $1 до $9 . Если вво­дится несколько аргументов, можно отдельно обращаться к каждому из них по его номеру. В следующем примере в командной строке вводятся три аргумента. Предварительно создадим скрипт arguments с аргументами, а потом его выполним.

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

Переменная export. Иногда для разных файлов-сценария требуется воспользоваться определенной переменной, которая уже была определена. Переменные, которые вы определяете в интерпретаторе shell, являются локальными для него. В некотором смысле такая пе­ременная принадлежит своему интерпретатору. Непосредственно определить переменную для другого интерпретатора нельзя, однако можно экспортировать определение переменной из одного интерпретатора в другой с помощью команды export. Команда export содержит инструкцию для системы, в соответствии с которой для каждого вновь образованного shell будет определяться копия этой переменной. Каждый новый интерпретатор shell будет иметь собственную копию экспортированной переменной. В следующем примере определим переменную «rrr» и экспортируем ее для других интерпретаторов с помощью команды export.

Арифметические операции – команда let . Команда let - это команда интерпретатора BASH shell, обеспечивающая выпол­нение операций над арифметическими величинами. С помощью этой команды можно сравнивать числовые значения или выполнять над ними арифметические операции, такие как сложение или умножение. Формат команды: let значение1 оператор значение2. Приведем пример.

$ let 2*5

10

В арифметические выражения, использующие оператор let, можно включать и опе­раторы присваивания. В следующем примере результат умножения двух чисел присваи­вается переменной total.

$ let “total=2*5”

$ echo $total

10

$

Операторы сравнения часто используются для сравнения числовых значений в управ­ляющих конструкциях, таких как циклы и условные переходы. В следующем примере команды сценария file1 четырежды выводят на экран слово "Привет!". В данном случае для управления выходом из цикла используется оператор let "ttt <= 4", а для увеличения переменной цикла again на единицу - оператор let "ttt = ttt + 1". Обратите внимание на то, что при изменении переменной again ее вычислять не требуется.

File1

ttt=l

while let "ttt<= 4"

do

echo $ttt Привет!

let "ttt = ttt + 1"

done

Выполнение скрипта:

$ file1

Привет!

Привет!

Привет!

Кроме того, вы также можете использовать и другие арифметические операторы. В таблице 1 приведены арифметические операторы и операторы сравнения Bash Shell.

Таблица 1 - Операторы BASH shell

Арифметические операторы

Функции

Умножение

Сложение

Вычитание

Деление с остатком

Операторы сравнения

Функции

Больше чем

Меньше чем

Больше либо равно

Меньше либо равно

Равенство в выражениях

Равенство в команде let

Не равно

Логическое И

Логическое ИЛИ

Логическое НЕ

Управляющие конструкции. Управляющие конструкции предназначены для управления ходом выполнения команд shell-сценария. Эти конструкции позволяют организовать повторное выполнение определенной последовательности команд или выбирать для выполнения команды, необходимые в конкретной ситуации. Управляющая конструкция состоит из двух основ­ных компонентов: операции проверки и команд. В результате выполнения сравнения (проверки условия) возвращается значение «истина» или «ложь», а затем на основании полученного результата выполняются определенные команды. Существует два вида управляющих конструкций: циклические (циклы) и условные (ус ловия). Циклическая конструкция используется для повторного выполнения команд, тогда как условная - для выполнения последовательности команд., которая удовлетво­ряет определенным условиям. В интерпретаторе BASH shell можно использовать три циклические конструкции, while, for и for-in, и две условные - if и case.

Управляющие конструкции while и if - это конструкции общего назначения, которые обычно используются при решении таких задач, как итерационные вычисления и проверка различных условий. Управляющие конструкции case и for ориентированы на более узкий круг задач. Конструкция case является многовариантным оператором и представляет собой частный случай условного оператора if. Эта конструкция часто используется при создании меню. Конструкция for представляет собой цикл, однократно обрабатывающий всю информацию для каждого значения, включенного в список, до тех пор, пока не встретится окончание списка.

Кроме сравнения значений или переменных, управляющие конструкции if и while можно применять для проверки того, успешно или неудачно была выполнена системная команда Linux. Напомним, что в Linux каждая выполняемая команда возвращает код завершения. Если выполнение команды было успешным, ее код завершения равен 0. Если по какой-либо причине команда не была выполнена успешно, кодом завершения будет некоторое положительное значение, указывающее тип ошибки. Управляющие кон­струкции if и while позволяют проверить, чему был равен код завершения: 0 или некоторому другому значению. Если код завершения равен нулю, значит, выполнение команды было успешным и управляющая конструкция if или while будет завершена.

Команда test . Сравнивать значения можно не только с помощью условных управляющих конструкций, но также с помощью команды test. При сравнении двух значений test возвращает 0 в том случае, если сравнение будет успешным. Команда test позволяет сравнивать целые числа, строки и даже выполнять логи­ческие операции. Совместно с командой test применяют опции, которые задают тип сравнения. Полный перечень опций приведен в таблице 2.

Таблица 2. Операции, выполняемые командой test интерпретатора BASH shell

Сравнение целых чисел

Функция

Больше чем

Меньше чем

Больше чем либо равно

Меньше чем либо равно

Не равно

Сравнение строк

Функция

Проверка на наличие пустой строки

Проверка на наличие строкового значения

Проверка строк на равенство

Прооверка строк на неравенство

Проверка на наличие строки, состоящей из нулей

Логические операции

Функция

Логическое И

Логическое ИЛИ

Логическое НЕ

Проверка файлов

Функция

Установка факта существования файла и его регулярности

Проверяется, не является ли файл пустым

Проверка возможности считывания из файла

Проверка возможности записи в файл, а также его изменения

Проверяется, является ли файл исполнимым

Проверяется, является ли имя файла именем каталога

Проверяется, является ли имя файла символической ссылкой

Проверяется, обозначает ли имя файла байт-ориентированное устройство

Проверяется, обозначает ли имя блок-ориентированное устройство

Команда test имеет следующий синтаксис:

test значение -опция значение

test строка = строка

В следующем примере покажем пример использования команды test. Сравним целочисленных значения, для этого используем опцию равенства –eq. Для проверки результата выполнения операции сравнения используем код завершения последней выполненной командыtest, который храниться в переменной $? интерпретатораshell.

$ tot = 4

$ test $ tot -eq 7

$ echo $?

1

Также команда test $tot –eq 7 может быть записана и в другом виде:

$ [ $tot –eq 7 ]

Условные конструкции: if, if-else, elif, case . Интерпретатор BASH shell включает несколько условных управляющих конструкций (табл. 3), которые позволяют выбирать для выполнения определенные команды Linux. Многие из этих конструкций напоминают условные управляющие конструкции в языках программирования, но имеются и некоторые отличия.

Таблица 3 – Управляющие конструкции интерпретаторов bash Shell

Условные управляющие конструкции

Функция

if команда then команда fi

Конструкция if вызывает выполнение действия в случае, если результат проверки истинен

if команда then команда else команда fi

Конструкция if-else вызывает выполнение действия в случае, если код завершения проверяемой команды paвен значению «истина», в противном случае выполняется действие else

if команда then команда elif команда then команда else команда fi

Конструкция elif дает возможность вкладывать конструкции if, что позволяет выбрать один из многих вариантов; если истинно условие, проверяемое первой конструкцией if, выполняются предусмотренные в ней команды и следующей конструкции elif управление не передается

case строка in шаблон) команда;; еsас

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

команда && команда

Логическая операция И возвращает значение 0 («истина»), если обе команды возвращают значение 0; если же одна из команд возвращает ненулевое значение, резуль­тат операции И равен «ложь» и данная операция воз­вращает ненулевое значение

команда | | команда

Логическая операция ИЛИ, возвращающая значение 0 («истина») в случае, если одна или обе команды возвра­щают значение 0 («истина»); если обе команды возвра­щают ненулевое значение, то результат операции ИЛИ - «ложь» и операция возвращает ненулевое значение

Команда

Логическая операция НЕ, инвертирует код завершения команды

while команда do команды done

Конструкция while выполняет действие до тех пор, пока команда проверки возвращает значение «истина»

until команда do команды done

Конструкция until выполняет действие до тех пор, пока команда проверки возвращает значение «ложь»

Циклические управляющие конструкции

Функция while, until, for, for-in, select

for переменная in список-значений do команды done

Конструкция for-in предназначена для обработки списка значений. Переменной последовательно присваиваются значения из списка

for переменная do команды done

Конструкция for предназначена для последовательной обработки аргументов сценария. Переменной последовательно присваивается значение каждого аргумента

select строка in перечень-элементов do команды done

Конструкция select создает меню на основе элементов заданного списка, а затем выполняется указанная команда (обычно это команда case)

Условная конструкция if - then . Условная конструкция if ставит условие на выполнение команды. Этим условием является код завершения какой-то конкретной команды Linux. Если команда выполнена успешно (то есть код завершения равен 0), то команды внутри конструкции if выполняются. Если код завершения отличен от 0, то команды внутри конструкции if выполняться не будут. Иногда требуется выбрать один из двух вариантов, в зависимости от того как была выполнены команда Linux. Ключевое слово else конструкции if позволяет выбрать один из двух вариантов. Приведем синтаксис команды if-then-else.

Как уже говорилось выше, для построения произвольных алгоритмов необходимо иметь операторы проверки условий. Оболочка bash поддерживает операторы выбора if then else и case , а также операторы организации циклов for , while , until , благодаря чему она превращается в мощный язык программирования.

5.8.1 Операторы if и test (или )

Конструкция условного оператора в слегка упрощенном виде выглядит так:

if list1 then list2 else list3 fi

где list1 , list2 и list3 — это последовательности команд, разделенные запятыми и оканчивающиеся точкой с запятой или символом новой строки. Кроме того, эти последовательности могут быть заключены в фигурные скобки: {list} .

Оператор if проверяет значение, возвращаемое командами из list1 . Если в этом списке несколько команд, то проверяется значение, возвращаемое последней командой списка. Если это значение равно 0, то будут выполняться команды из list2 ; если это значение не нулевое, будут выполнены команды из list3 . Значение, возвращаемой таким составным оператором if , совпадает со значением, выдаваемым последней командой выполняемой последовательности.

Полный формат команды if имеет вид:

if list then list [ elif list then list ] ... [ else list ] fi

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

В качестве выражения, которое стоит сразу после if или elif , часто используется команда test , которая может обозначаться также квадратными скобками . Команда test выполняет вычисление некоторого выражения и возвращает значение 0, если выражение истинно, и 1 в противном случае. Выражение передается программе test как аргумент. Вместо того, чтобы писать

test expression,

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

[ expression ].

Заметьте, что test и [ — это два имени одной и той же программы, а не какое-то магическое преобразование, выполняемое оболочкой bash (только синтаксис [ требует, чтобы была поставлена закрывающая скобка). Заметьте также, что вместо test в конструкции if может быть использована любая программа.

В заключение приведем пример использования оператора if :

if [ -e textmode2.htm ] ; then

ls textmode*

else

pwd

Об операторе test (или […]) надо бы поговорить особо.

5.8.2 Оператор test и условные выражения

Условные выражения, используемые в операторе test , строятся на основе проверки файловых атрибутов, сравнения строк и обычных арифметических сравнений. Сложные выражения строятся из следующих унарных или бинарных операций ("элементарных кирпичиков"):

    A file

Верно, если файл с именем file существует.

    B file

Верно, если file существует и является специальным файлом блочного устройства.

    C file

Верно, если file существует и является специальным файлом символьного устройства.

    D file

Верно, если file существует и является каталогом.

    E file

Верно, если файл с именем file существует.

    F file

Верно, если файл с именем file существуети является обычным файлом.

    G file

Верно, если файл с именем file существуети для него установлен бит смены группы.

    H file или -L file

Верно, если файл с именем file существуети является символической ссылкой.

    K file

Верно, если файл с именем file существуети для него установлен "sticky"" bit.

    P file

Верно, если файл с именем file существуети является именованным каналом (FIFO).

    R file

Верно, если файл с именем file существуети для него установлено право на чтение

    S file

Верно, если файл с именем file существуети его размер больше нуля .

    T fd

Верно, если дескриптор файла fd открыт и указывает на терминал.

    U file

Верно, если файл с именем file существуети для него установлен бит смены пользователя.

    W file

Верно, если файл с именем file существуети для него установлено право на запись.

    X file

Верно, если файл с именем file существуети является исполняемым .

    O file

Верно, если файл с именем file существуети его владельцем является пользователь, на которого указывает эффективный идентификатор пользователя.

    G file

Верно, если файл с именем file существуети принадлежит группе, определяемой эффективным идентификатором группы.

    S file

Верно, если файл с именем file существуети является сокетом.

    N file

Верно, если файл с именем file существуети изменялся с тех пор, как был последний раз прочитан.

    file1 -nt file2

Верно, если файлfile1 имеет более позднее время модификации, чем file2 .

    file1 -ot file2

Верно, если файлfile1 старше , чем file2 .

    file1 -ef file2

Верно, если файлыfile1 и file2 имеют одинаковые номера устройств и индексных дескрипторов (inode).

    O optname

Верно, если задействована опция оболочки optname . Пояснения см. на странице man bash.

    Z string

Верно, если длина строки равна нулю.

    N string

Верно, если длина строки не равна нулю.

    string1 == string2

Верно, если строки совпадают. Вместо == может использоваться = .

    string1 !== string2

Верно, если строки не совпадают.

    string1 < string2

Верно, если строка string1 лексикографически предшествует строке string2 (для текущей локали).

    string1 > string2

Верно, если строка string1 лексикографически стоит после строки string2 (для текущей локали).

    arg1 OP arg2

Здесь OP — это одна из операций арифметического сравнения: -eq (равно), -ne (не равно), -lt (меньше чем), -le (меньше или равно), -gt (больше), -ge (больше или равно). В качестве аргументов могут использоваться положительные или отрицательные целые.

Из этих элементарных условных выражений можно строить сколь угодно сложные с помощью обычных логических операций ОТРИЦАНИЯ, И и ИЛИ:

    !(expression)

Булевский оператор отрицания.

    expression1 -a expression2

Булевский оператор AND (И). Верен, если верны оба выражения.

    expression1 -o expression2

Булевский оператор OR (ИЛИ). Верен, если верно любое из двух выражений.

Такие же условные выражения используются и в операторах while и until , которые мы рассмотрим чуть ниже.

5.8.3 Оператор case

Формат оператора case таков:

case word in [ [(] pattern [ | pattern ] ...) list ;; ] ... esac

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

Следующий пример использования оператора case заимствован из системного скрипта /etc/rc.d/rc.sysinit.

case "$UTC" in

yes|true)

CLOCKFLAGS="$CLOCKFLAGS -u";

CLOCKDEF="$CLOCKDEF (utc)";

no|false)

CLOCKFLAGS="$CLOCKFLAGS --localtime";

CLOCKDEF="$CLOCKDEF (localtime)";

esac

Если переменная принимает значение yes или true, то будет выполнена первая пара команд, а если ее значение равно no или false - вторая пара.

5.8.4 Оператор select

Оператор select позволяет организовать интерактивное взаимодействие с пользователем. Он имеет следующий формат:

select name [ in word; ] do list ; done

Вначале из шаблона word формируется список слов, соответствующих шаблону. Этот набор слов выводится в стандартный поток ошибок, причем каждое слово сопровождается порядковым номером. Если шаблон word пропущен, таким же образом выводятся позиционные параметры. После этого выдается стандартное приглашение PS3, и оболочка ожидает ввода строки на стандартном вводе. Если введенная строка содержит число, соответствующее одному из отображенных слов, то переменной name присваивается значение, равное этому слову. Если введена пустая строка, то номера и соответствующие слова выводятся заново. Если введено любое другое значение, переменной name присваивается нулевое значение. Введенная пользователем строка запоминается в переменой REPLY . Список команд list выполняется с выбранным значением переменной name .

Вот небольшой скрипт:

#!/bin/sh

echo "Какую ОС Вы предпочитаете?"

select var in "Linux" "Gnu Hurd" "Free BSD" "Other"; do

break

done

echo "Вы бы выбрали $var"

Какую ОС Вы предпочитаете?
1) Linux
2) Gnu Hurd
3) Free BSD
4) Other
#?

Нажмите любую из 4 предложенных цифр (1,2,3,4). Если вы, например, введете 1, то увидите собщение:

“Вы бы выбрали Linux”

5.8.5 Оператор for

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

for name in words do list done.

Правила построения списков команд (list ) такие же, как и в операторе if .

Пример. Следующий скрипт создает файлы foo_1, foo_2 и foo_3:

for a in 1 2 3 ; do

touch foo_$a

done

В общем случае оператор for имеет формат:

for name [ in word; ] do list ; done

Вначале производится раскрытие слова word в соответствии с правилами раскрытия выражений, приведенными выше. Затем переменной name поочередно присваиваются полученные значения, и каждый раз выполняется список команд list . Если "in word " пропущено, то список команд list выполняется один раз для каждого позиционного параметра, который задан.

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

for a in $(seq 1 10) ; do

cat file_$a

done

Эта команда выводит на экран содержимое 10-ти файлов: " file_1", ..., "file_10".

5.8.6 Операторы while и until

Оператор while работает подобно if , только выполнение операторов из списка list2 циклически продолжается до тех пор, пока верно условие, и прерывается, если условие не верно. Конструкция выглядит следующим образом:

while list1 do list2 done.

while [ -d mydirectory ] ; do

ls -l mydirectory >> logfile

echo -- SEPARATOR -- >> logfile

sleep 60

done

Такая программа будет протоколировать содержание каталога "mydirectory" ежеминутно до тех пор, пока директория существует.

Оператор until аналогичен оператору while :

until list1 do list2 done.

Отличие заключается в том, что результат, возвращаемый при выполнении списка операторов list1 , берется с отрицанием: list2 выполняется в том случае, если последняя команда в списке list1 возвращает ненулевой статус выхода.

5.8.7 Функции

Синтаксис

Оболочка bash позволяет пользователю создавать собственные функции. Функции ведут себя и используются точно так же, как обычные команды оболочки, т. е. мы можем сами создавать новые команды. Функции конструируются следующим образом:

function name () { list }

Причем слово function не обязательно, name определяет имя функции, по которому к ней можно обращаться, а тело функции состоит из списка команд list , находящегося между { и }. Этот список команд выполняется каждый раз, когда имя name задано как имя вызываемой команды. Отметим, что функции могут задаваться рекурсивно, так что разрешено вызывать функцию, которую мы задаем, внутри нее самой.

Функции выполняются в контексте текущей оболочки: для интерпретации функции новый процесс не запускается (в отличие от выполнения скриптов оболочки).

Аргументы

Когда функция вызывается на выполнение, аргументы функции становятся позиционными параметрами (positional parameters) на время выполнения функции. Они именуются как $n , где n — номер аргумента, к которому мы хотим получить доступ. Нумерация аргументов начинается с 1, так что $1 — это первый аргумент. Мы можем также получить все аргументы сразу с помощью $* , и число аргументов с помощью $# . Позиционный параметр 0 не изменяется.

Если в теле функции встречается встроенная команда return , выполнение функции прерывается и управление передается команде, стоящей после вызова функции. Когда выполнение функции завершается, позиционным параметрам и специальному параметру # возвращаются те значения, которые они имели до начала выполнения функции.

Локальные переменные (local)

Если мы хотим создать локальный параметр, можно использовать ключевое слово local . Синтаксис ее задания точно такой же, как и для обычных параметров, только определению предшествует ключевое слово local: local name=value .

Вот пример задания функции, реализующей упоминавшуюся выше команду seq :

seq()

local I=$1;

while [ $2 != $I ]; do

echo -n "$I ";

I=$(($I + 1))

done;

echo $2

Обратите внимание на опцию -n оператора echo , она отменяет переход на новую строку. Хотя это и несущественно для тех целей, которые мы здесь имеем в виду, это может оказаться полезным для использования функции в других целях.

Функция вычисления факториала fact

Еще один пример:

fact()

if [ $1 = 0 ]; then

echo 1;

else

echo $(($1 * $(fact $(($1 — 1)))))

Это функция факториала, пример рекурсивной функции. Обратите внимание на арифметическое расширение и подстановку команд.

В. Костромин (kos at rus-linux dot net) - 5.8. Shell как язык программирования

Что такое 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