Лабораторная работа 10 Разработка сценариев bash
Автор: student | Категория: Технические науки / Информатика и программирование | Просмотров: 2695 | Комментирии: 0 | 06-03-2014 20:14

Лабораторная работа 10

 

Разработка сценариев bash

 

Цель работы – практическое знакомство с методами создания и использования сценариев ОС Linux

  1. КРАТКИЕ ТЕОРЕТИЧЕСКИЕ СВЕДЕНИЯ

 

1.1.            Необходимость использования сценариев командной оболочки

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

Хотя внешне командная оболочка очень похожа на режим командной строки в ОС Windows, она гораздо мощнее и способна выполнять самостоятельно очень сложные программы. Командная оболочка выполняет программы оболочки, часто называемые сценариями или скриптами, которые интерпретируются во время выполнения. Такой подход облегчает отладку, потому что можно выполнять программу построчно и не тратить время на перекомпиляцию. Но для задач, которым важно время выполнения или необходимо интенсивное использование процессора, командная оболочка оказывается неподходящей средой.

1.2 Командная оболочка

 

Командная оболочка — это программа, которая действует как интерфейс между пользователем и ОС Linux, позволяя вводить команды, которые должна выполнить операционная система. В ОС Linux вполне может сосуществовать несколько установленных командных оболочек, и разные пользователи могут выбрать ту, которая им больше нравится. Поскольку ОС Linux — модульная система, можно вставить и применять одну из множества различных стандартных командных оболочек. В Lmux стандартная командная оболочка, всегда устанавливаемая как /bin/sh и входящая в комплект средств проекта GNU, называется bash (GNU Bourne-Again SHell). В данной работе используется оболочка bash версии 3, ее функциональные возможности являются общими для всех командных оболочек, удовлетворяющих требованиям стандарта POSIX.

 

 

Рис. 1. Укрупненная архитектура ОС Linux

 

Каналы и перенаправление

Прежде чем заняться подробностями программ командной оболочки, необходимо сказать несколько слов о возможностях перенаправления ввода и вывода программ (не только программ командной оболочки) в ОС Linux.

 

Перенаправление вывода

Ранее были рассмотрены некоторые виды перенаправления, например, такие как:

$ ls -l > lsoutput.txt

сохраняющие вывод команды ls в файле с именем lsoutput.txt.

Однако перенаправление позволяет сделать гораздо больше, чем демонстрирует этот простой пример. Cейчас  нужно знать только то, что дескриптор файла 0 соответствует стандартному вводу программы, дескриптор файла 1 — стандартному выводу, а дескриптор файла 2 — стандартному потоку ошибок. Каждый из этих файлов можно перенаправлять независимо друг от друга. На.самом деле можно перенаправлять и другие дескрипторы файлов, но, как правило, нет нужды перенаправлять любые другие дескрипторы, кроме стандартных: 0, 1 и 2.

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

$ ps >> lsoutput.txt

добавит вывод команды ps в конец заданного файла. В этом примере и далее знак $ перед командой – приглашение ОС Linux.

Для перенаправления стандартного потока ошибок перед оператором > вставьте номер дескриптора файла, который хотите перенаправить. Поскольку у стандартного потока ошибок дескриптор файла 2, укажите оператор 2>. Часто бывает полезно скрывать стандартный поток ошибок, запрещая вывод его на экран.

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

Команда  $ kill -HUP 1234 > killout.txt 2>killer.txt

поместит вывод и информацию об ошибке в разные файлы.

Если вы предпочитаете собрать оба набора выводимых данных в одном файле, можно применить оператор >2 для соединения двух выводных потоков. Таким образом, команда

$ kill -1 1234 > killerr.txt 2>41

поместит свой вывод и стандартный поток ошибок в один и тот же файл. Обратите внимание на порядок следования операторов. Приведенный пример читается как "перенаправить стандартный вывод в файл killerr.txt, а затем перенаправить стандартный поток ошибок туда же, куда и стандартный вывод". Если вы нарушите порядок, перенаправление выполнится не так, как вы ожидаете.

Поскольку обнаружить результат выполнения команды kill можно с помощью кода завершения, часто не потребуется сохранять какой бы то ни было стандартный вывод или стандартный поток ошибок. Для того чтобы полностью отбросить любой вывод, можно использовать универсальную "мусорную корзину" Linux, /dev/null, следующим образом:

$ kill -1 1234 >/dev/null  2>fil

 

Перенаправление ввода

Также как вывод можно перенаправить ввод. Например  $ more < killout.txt

 

Каналы

Процессы можно соединять с помощью оператора канала | . Как пример, можно применить команду sort для сортировки вывода команды ps.

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

$ ps > psout.txt

$ sort psout.txt > pasoirt.out

Соединение процессов каналом даст более элегантное решение:

$ ps | sort > pssort.out

При желании увидеть на экране вывод, разделенный на страницы, можно подсоединить третий процесс, more:

$ ps  |  sort  |  more

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

$ ps -xо comm | sort | uniq | grep -v sh | more

В ней берется вывод команды ps, сортируется в алфавитном порядке, из него извлекаются процессы с помощью команды uniq, применяется утилита grep -v sh для удаления процесса с именем sh и в завершение полученный список постранично выводится на экран. Это более элегантное решение, чем строка из отдельных команд, каждая со своими временными файлами.

 

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

 

Есть два способа написания программ оболочки. Вы можете ввести последовательность команд и разрешить командной оболочке выполнить их в интерактивном режиме или сохранить эти команды в файле и затем запускать его как программу.

Интерактивные программы

Легкий и очень полезный во время обучения или тестирования способ проверить работу небольших фрагментов кода— просто набрать с клавиатуры в командной строке сценарий командной оболочки.

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

$ for file in *

> do

> if grep -l POSIX $file

> then

> more $file

> fi

> done posix

This is a file with POSIX in it - treat it well

$

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

В этом примере команда grep выводит на экран найденные ею имена файлов, содержащих строку posix, а затем команда more отображает на экране содержимое файла. В конце на экран возвращается приглашение командной оболочки. Обратите внимание также на то, что вы ввели переменную командной оболочки, которая обрабатывает каждый файл для самодокументирования сценария. С таким же успехом можно использовать переменную i, но имя file более информативно с точки зрения пользователей.

Командная оболочка также обрабатывает групповые символы или метасимволы (часто называемые знаками подстановки). Например, символ * - знак подстановки, соответствующий строке символов, односимвольный знак подстановки ? соответствует одиночному символу. Подстановочный шаблон из фигурных скобок {} позволяет формировать множество из произвольных строк, которое командная оболочка раскроет. Например, команда

$  ls my_{finger, toe}s

будет проверять файлы с именами my_figers и my_toes в текущем каталоге.

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

 

Создание сценария

Создать файл, содержащий команды, можно помощью любого текстового редактора. В данной работе рекомендуется использовать встроенный в mc редактор. Для создания нового файла в mc используйте комбинацию клавиш Shift+F4. Создайте файл с именем first с таким содержимым:

#!/bin/sh

# first

# Этот файл просматривает все файлы в текущем каталоге для поиска строки

# POSIX, а затем выводит имена найденных файлов в стандартный вывод.

for file in *

do

  if grep -q POSIX $file

  then

   echo $file

  fi

done

exit 0

Комментарий начинается со знака # и продолжается до конца строки. Принято знак # ставить в первой символьной позиции строки. Первая строка #! /bin/sh — это особая форма комментария; символы #! сообщают системе о том, что следующий за ними аргумент — программа, применяемая для выполнения данного файла. В данном случае программа /bin/sh — командная оболочка, применяемая по умолчанию.

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

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

В сценарии не используются никакие расширения и суффиксы имен файлов; ОС Linux и UNIX, как правило, редко применяют при именовании файлов расширения для указания типа файла.

 

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

Файл сценария можно выполнить двумя способами. Более простой путь — запустить оболочку с именем файла сценария как параметром:

$ /bin/sh first

Этот вариант будет работать, но лучше запускать сценарий, введя его имя и тем самым присвоив ему статус других команд Linux. Сделать это можно с помощью команды chmod, изменив режим файла (file mode) и сделав его исполняемым для всех пользователей:

$ chmod +x first

После этого можно выполнять файл с помощью команды $ first

При этом может появиться сообщение об ошибке, говорящее о том, что команда не найдена. Исправить ошибку можно введя с клавиатуры в командной строке ./first в каталоге, содержащем сценарий, чтобы задать командной оболочке полный относительный путь к файлу.

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

После того как вы убедитесь в корректной работе вашего сценария, можете переместить его в более подходящее место, чем текущий каталог. Если команда предназначена только для собственных нужд, можете создать каталог bin в своем исходном каталоге и добавить его в свой путь. Если вы хотите, чтобы сценарий выполняли другие пользователи, можно использовать каталог /usr/local/bin или другой системный каталог как удобное хранилище для вновь созданных программ.

 

1.4. Синтаксис языка командной оболочки

 

Переменные

В командной оболочке переменные перед применением обычно не объявляются. Вместо этого они создаются (например, когда им присваивается начальное значение). По умолчанию все переменные считаются строками и хранятся как строки, даже когда им присваиваются числовые значения. Командная оболочка и некоторые утилиты преобразуют строки, содержащие числа, в числовые значения, когда с переменными нужно выполнить арифметические операции. Командная оболочка считает fоо и Foo двумя разными переменными, отличающимися от третьей переменной FOO.

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

 

$ salut=Hello

$ echo $salut

Hello

$ salut="Yes Dear"

$ echo $salut

Yes Dear

$ salut=7+5

$ echo $salut

7+5

Примечания

1. При наличии пробелов в содержимом переменной ее заключают в кавычки. Кроме того, не может быть пробелов справа и слева от знака равенства.

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

 

$ echo $((7+5))

12

$ a=5

$ b=4

$ echo $(((a+b)*(a-b)))

9

С помощью команды read можно присвоить переменной пользовательский ввод. Команда принимает один параметр — имя переменной, в которую будут считываться данные и затем ждет, пока пользователь введет какой-либо текст. Команда read обычно завершается после нажатия пользователем клавиши <Enter>. При чтении переменной с терминала, как правило, заключать ее значения в кавычки не требуется:

$ read salut

Wie geht's?

$ echo $salut

Wie geht's?

 

Правила использования кавычек

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

Поведение переменных, таких как $foo, заключенных в кавычки, зависит от вида используемых кавычек. Если вы заключаете в двойные кавычки $-представление переменной, оно во время выполнения командной строки заменяется значением переменной. Если вы заключаете его в одинарные кавычки или апострофы, никакой замены не происходит.

 

Пример 1.

В этом примере показано, как кавычки влияют на вывод переменной:

#!/bin/sh

myvar="Hi there"

echo $myvar

echo "$myvar"

echo '$myvar'

echo \$myvar

echo Enter some text

read myvar

echo '$myvar' $myvar

exit 0

Данный сценарий ведет себя следующим образом:

$ ./variable

Hi there

Hi there

$myvar

$myvar

Enter some text

Hello world

$myvar Hello World

В сценарии создается переменная myvar, и ей присваивается строка Hi there. Содержимое пе­ременной выводится на экран с помощью команды echo, демонстрирующей, как символ $ раскрывает содержимое переменной.  Применение двойных кавычек не влияет на раскрытие содержимого переменной, а одинарные кавычки и обратный слэш влияют. Показано использование команды read для получения строки от пользователя.

 

Переменные окружения

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

$НОМЕ Исходный каталог текущего пользователя

$РАТН  Разделенный двоеточиями список каталогов для поиска команд

$PS1    Подсказка или приглашение командной строки. Часто это знак $, но в оболочке bash можно применять и более сложные варианты. Например, строка [\u@\h \w]$ — популярный стандарт, сообщающий в подсказке пользователю имя компьютера и текущий каталог, а также знак $.

$PS2 Дополнительная подсказка или приглашение, применяемое как приглашение для дополнительного ввода; обычно знак >

$#   Количество передаваемых параметров

 

Переменные-параметры

Если сценарий вызывается с параметрами, создается несколько дополнительных переменных. Если параметры не передаются, переменная окружения $# равна 0.

Переменные-параметры перечислены в табл. 1.

Таблица 1

Переменная-параметр

Описание

$1, $2, ...

Параметры, передаваемые сценарию

$*

Список всех параметров в единственной переменной, разделенных первым символом из переменной окружения ifs.

 

Условия

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

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

 

Команда test или [

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

Поскольку команда test не часто применяется за пределами сценариев командной оболочки, многие пользователи ОС Linux, никогда раньше не писавшие сценариев пытаются создавать простые программы и называют их test. Если такая программа не работает, вероятно, она конфликтует с командой оболочки test.

Представим команду test на примере одного простейшего условия: проверки наличия файла. Для нее понадобится следующая команда: test -f <имя_файла>, поэтому в сценарии можно написать

if test -f fred.c then

fi

To же самое можно записать следующим образом:

if [ -f fred.c ] then

fi

Код завершения команды test (выполнено ли условие) определяет, будет ли выполняться условный программный код.

Необходимо вставлять пробелы между квадратной скобкой [ и про­веряемым условием. Это легко усвоить, если запомнить, что вставить символ [ — это все равно, что написать test, а после имени команды всегда нужно вставлять  пробел.

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

if   [  -f fred.c  ];   then fi

Варианты условий, которые используются в команде test, делятся на три типа:

  • строковые сравнения,
  • числовые сравнения,
  • проверка файловых флагов.

Эти условия описаны в табл. 2.

Таблица 2. Условия

Варианты условий

Результат

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

Строка1 = Строка 2

True (истина), если строки одинаковы

Строка1 != Строка2

True (истина), если строки разные

-n Строка

True (истина), если Строка не null

-z Строка

True (истина), если Строка null (пустая строка)

Сравнения чисел

 Выр1 -eq Выр2

True (истина), если выражения равны

 Выр1 -nе Выр2

True (истина), если выражения не равны

Выр1 -gt Выр2

True (истина), если Выр1 больше, чем Выр2

Выр1 -gе Выр2

True (истина), если Выр1 не меньше Выр2

Выр1 -lt Выр2

True (истина), если Выр1 меньше, чем Выр2

Выр1 -le Выр2

True (истина), если Выр1 не больше Выр2

!Выражение

True (истина), если Выражение ложно, и наоборот

Файловые флаги

-d файл

True (истина), если файл— каталог

-е файл

True (истина), если файл существует.

-f файл

True (истина), если файл— обычный файл

-r файл

True (истина), если файл доступен для чтения

-s файл

True (истина), если файл ненулевого размера

-w файл

True (истина), если файл доступен для записи

-х файл

True (истина), если файл— исполняемый файл

     

 

Пример 2 тестирования состояния файла /bin/bash.

#!/bin/sh

if [ -f /bin/bash ]

then

echo "file  /bin/bash exists"

fi

 

if [ -d /bin/bash  ]

then

  echo  "/bin/bash  is a directory"

else

    echo "/bin/bash is NOT a directory"

fi

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

 

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

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

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

 

Оператор разветвления if

Оператор if очень прост: он проверяет результат выполнения команды и затем в зависимости от условия выполняет ту или иную группу операторов.

if условие then

операторы

else

операторы

fi

Наиболее часто оператор if применяется, когда задается вопрос, и решение принимается в зависимости от ответа.

Пример 3

#!/bin/sh

echo "Сейчас утро? Ответьте yes или no"

read timeofday

if [ $timeofday = "yes" ]; then

   echo "Доброе утро"

else

   echo "Добрый вечер"

fi

exit 0

В результате будет получен следующий вывод на экран:

Сейчас утро? Ответьте yes или no yes

yes

Доброе утро

$

В этом сценарии для проверки содержимого переменной timeofday применяется команда [. Результат оценивается оператором if, который затем разрешает выполнять разные строки программного кода.

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

 

Конструкция elif

К сожалению, с этим простым сценарием связано несколько проблем. Во-первых, он принимает в значении nо (нет) любой ответ за исключением yes (да). Можно усовершенствовать сценарий, воспользовавшись конструкцией elif, которая позволяет добавить второе условие, проверяемое при выполнении части else оператора if  (пример 4).

Можно откорректировать предыдущий сценарий так, чтобы он выводил сообщение об ошибке, если пользователь вводит что-либо отличное от yes или nо. Для этого следует заменить ветку else веткой elif и добавить еще одно условие:

Пример 4

#!/bin/sh

echo "Сейчас утро? Ответьте yes или no"

read timeofday

if  [ $timeofday = "yes" ]

then

echo  "Доброе утро"

elif [ $timeofday = "no" ]; then

  echo "Добрый вечер "

else

 echo "Извините, $timeofday не распознается. Ответьте yes или no "

exit 1

fi

exit 0

 

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

Проблема, связанная со значением переменной

Данный сценарий исправляет наиболее очевидный дефект, а более тонкая проблема остается незамеченной. Запустите новый вариант сценария, но вместо ответа на вопрос просто нажмите клавишу <Enter> . Вы получите сообщение об ошибке:

[: = : unary operator expected

Что же не так? Проблема в первой ветви оператора if. Когда проверялась переменная timeofday, она состояла из пустой строки. Следовательно, ветвь оператора if выглядела следующим образом: if [ = "yes" ] и не представляла собой верное условие. Во избежание этого следует заключить имя переменной в кавычки:  if [ "$timeofday" = "yes" ]

Теперь проверка с пустой переменной будет корректной:

if [ "" = "yes" ]

Новый сценарий будет таким:

Пример 5.

#!/bin/sh

echo " Сейчас утро? Ответьте yes или no "

read timeofday

if  [ "$timeofday " = "yes" ]

then

echo  "Доброе утро"

elif [ "$timeofday" = "no" ]; then

  echo "Добрый вечер "

else

 echo "Извините, $timeofday не распознается. Ответьте yes или no "

exit 1

fi

exit 0

 

Этот вариант безопасен, даже если пользователь в ответ на вопрос просто нажмет клавишу <Enter>.

Примечание. Если вы хотите, чтобы команда echo не переходила на новую строку, наиболее переносимый вариант— применить команду printf (см. раздел "printf" далее) вместо команды echo. В оболочке bash для запрета перехода на новую строку допускается команда echo –n. Поэтом