Текст книги "Основы программирования в Linux"
Автор книги: Нейл Мэтью
Соавторы: Ричард Стоунс
Жанры:
Программирование
,сообщить о нарушении
Текущая страница: 9 (всего у книги 67 страниц)
Особый способ передачи из сценария командной оболочки входных данных команде – использование встроенного документа (here document). Такой документ позволяет команде выполняться так, как будто она читает данные из файла или с клавиатуры, в то время как на самом деле она получает их из сценария.
Встроенный документ начинается со служебных символов <<
, за которыми следует специальная символьная последовательность, повторяющаяся и в конце документа. Символы <<
обозначают в командной оболочке перенаправление данных, которое в данном случае заставляет вход команды превратиться во встроенный документ. Специальная последовательность символов действует как маркер, указывая оболочке, где завершается встроенный документ. Маркерная последовательность не должна включаться в строки, передаваемые команде, поэтому лучше всего сделать ее запоминающейся и четко выделяющейся.
Рассмотрим упражнение 2.19.
Упражнение 2.19. Применение встроенных документов
Простейший пример просто передает входные данные команде cat
.
#!/bin/sh
cat <
hello
this is a here
document
!FUNKY!
Этот пример выводит на экран следующие строки:
hello
this is a here
document
Встроенные документы могут показаться странным средством, но они очень полезны, т.к. позволяют запускать интерактивные программы, например редактор, и снабжать их каким-либо заранее определенным вводом. Но особенно часто они применяются при выводе из сценария больших порций текста, как вы уже видели, и при этом позволяют избавиться от необходимости применять команду echo
для каждой выводимой строки. Вы можете использовать восклицательные знаки (!
) с двух сторон от идентификатора документа во избежание путаницы.
Если вы хотите обработать несколько строк заранее определенным способом, можно применить в сценарии строчный редактор ed и передать ему команды из встроенного документа (упражнение 2.20).
Упражнение 2.20. Ещё одно применение встроенного документа
1. Начнем с файла, названного a_text_file и содержащего следующие строки:
That is line 1
That is line 2
That is line 3That is line 4
2. Вы можете отредактировать этот файл, совместно используя встроенный документ и редактор ed:
#!/bin/sh
ed a_text_file <
3
d
., $s/is/was/ w
q
!FunkyStuff!
exit 0
Если вы выполните этот сценарий, то увидите, что теперь файл содержит следующие строки:
That is line 1
That is line 2
That was line 4
Как это работает
Сценарий командной оболочки запускает редактор ed и передает ему команды, необходимые для перехода к третьей строке, удаления строки и затем замены ее содержимым текущей строки (поскольку строка 3 (line 3) была удалена, теперь текущая строка – последняя строка файла). Эти команды редактора ed берутся из строк сценария, формирующих встроенный документ, строк между маркерами !Funky Stuff!
.
Отладка сценариевПримечание
Обратите внимание на знак
внутри встроенного документа, применяемый для защиты от подстановки, выполняемой командной оболочкой. Символ
экранирует знак
$
, поэтому оболочка знает, что не следует пытаться подставить вместо строки$s/is/was/
ее значение, которого у нее конечно же нет. Оболочка просто передает текст$
как $, который затем сможет интерпретировать редактор e
Обычно отлаживать сценарии командной оболочки довольно легко, хотя специальных вспомогательных средств отладки не существует. В этом разделе мы дадим краткий обзор наиболее распространенных приемов.
Когда возникает ошибка, командная оболочка, как правило, выводит на экран номер строки, содержащей ошибку. Если ошибка сразу не видна, вы можете добавить несколько дополнительных команд echo
для отображения содержимого переменных и протестировать фрагменты программного кода, просто вводя их в командной оболочке в интерактивном режиме.
Поскольку сценарии обрабатываются интерпретатором, нет затрат на компиляцию при корректировке и повторном выполнении сценария. Основной способ отслеживания наиболее трудно выявляемых ошибок – задание различных опций командной оболочки. Для этого вы можете применять опции командной строки после запуска командной оболочки или использовать команду set
. В табл. 2.19 перечислены эти опции.
Таблица 2.19
set | ||
---|---|---|
sh -n <сценарий> | set -о noexec set -n | Только проверяет синтаксические ошибки; не выполняет команды |
sh -v <сценарий> | set -о verbose set -v | Выводит на экран команды перед их выполнением |
sh -х <сценарий> | set -о xtrace set -x | Выводит на экран команды после обработки командной строки |
sh -u <сценарий> | set -o nounset set -u | Выдает сообщение об ошибке при использовании неопределенной переменной |
Вы можете установить опции с помощью флагов -о
и сбросить их с помощью флагов +о
подобным же образом в сокращенных версиях. Получить простое отслеживание выполнения можно, используя опцию xtrace
. Для начала вы можете применить опцию командной строки, но для более тщательной отладки следует поместить опции xtrace
(задавая выполнение и сброс отслеживания выполнения) внутрь сценария, в тот фрагмент кода, который создает проблему. Отслеживание выполнения заставляет командную оболочку перед выполнением каждой строки сценария выводить на экран эту строку и подставлять в нее значения используемых переменных.
Для установки опции xtrace
используйте следующую команду:
set -о xtrace
Для того чтобы снова отключить эту опцию, применяйте следующую команду:
set +о xtrace
Уровень выполняемых подстановок задается (по умолчанию) количеством знаков +
в начале каждой строки. Вы можете заменить знак +
на что-то более осмысленное, определив переменную командной оболочки PS4
в вашем файле конфигурации оболочки.
В командной оболочке также можно выяснить состояние программы после ее завершения, перехватив сигнал EXIT
с помощью строки, подобной приведенной далее и помещенной в начале вашего сценария:
trap 'echo Exiting: critical variable = $critical_variable' EXIT
По направлению к графическому режиму – утилита dialog
Прежде чем мы закончим обсуждение сценариев командной оболочки, обратимся к еще одному средству, которое, хотя, строго говоря, и не является частью оболочки, в основном полезно применять именно в программах командной оболочки.
Если вы знаете, что ваш сценарий придется выполнять только с консоли ОС Linux, существует довольно изящный способ оживить сценарий, применяя служебную команду dialog
. Она использует средства псевдографики текстового режима и цвет, но при этом выглядит привлекательно.
Примечание
В некоторых дистрибутивах команда
dialog
по умолчанию не устанавливается; например, в Ubuntu вам, возможно, придется добавить совместно поддерживаемые репозитарии для поиска готовой версии. В других дистрибутивах вы можете найти уже установленный альтернативный вариант,gdialog
. Он очень похож, но рассчитан на пользовательский интерфейс GNOME, применяемый для отображения диалоговых окон команды. В этом случае вы получите настоящий графический интерфейс. Как правило, в любой программе, использующей командуdialog
, можно заменить все вызовы этой команды наgdialog
, и вы получите графическую версию вашей программы. В конце этого раздела мы покажем пример программы, использующей командуgdialog
.
Общая концепция утилиты dialog
проста – одна программа с множеством параметров и опций, позволяющих отображать различные типы графических окон, начиная с простых окон с кнопками типа Yes/No (Да/Нет) и заканчивая окнами ввода и даже выбором пункта меню. Утилита обычно возвращает результат, когда пользователь выполнил какой-либо ввод, и результат может быть получен или из статуса завершения, или, если вводился текст, извлечением стандартного потока ошибок.
Прежде чем переходить к подробностям, давайте рассмотрим очень простой пример применения утилиты dialog
. Вы можете вызывать ее непосредственно из командной строки, что прекрасно с точки зрения создания прототипов. Поэтому создадим простое окно сообщений для отображения традиционной первой программы:
dialog –msgbox «Hello World» 9 18
На экране появится графическое информационное окно, дополненное кнопкой OK (рис. 2.3).
Рис. 2.3
Теперь, когда вы убедились в простоте утилиты dialog
, давайте поподробнее рассмотрим ее функциональные возможности. Основные типы диалоговых окон, которые вы можете создавать, перечислены в табл. 2.20.
Таблица 2.20
Окна с флажками (Check boxes) | –checklist | Позволяет отображать список флажков, каждый из которых можно установить или сбросить |
Информационные окна (Info boxes) | –infobox | Простое немедленное отображение в окне, без очистки экрана, возвращаемых данных |
Окна ввода (Input boxes) | –inputbox | Позволяет пользователю вводить в окно текст |
Окна меню (Menu boxes) | –menu | Позволяет пользователю выбрать один пункт из списка |
Окна сообщений (Message boxes) | –msgbox | Отображает сообщения для пользователей и снабжено кнопкой OK, которую они должны нажать для продолжения |
Окна с переключателями (Radio selection boxes) | –radiolist | Позволяет пользователю выбрать один переключатель из списка |
Текстовые окна (Text boxes) | –textbox | Позволяют отображать содержимое файла в окне с прокруткой |
Диалоговые окна Да/Нет (Yes/No boxes) | –yesno | Позволяют задать вопрос, на который пользователь может ответить «Да» или «Нет» |
Доступны также и некоторые дополнительные типы диалоговых окон (например, индикатор процесса или окно ввода пароля). Если вы хотите узнать побольше о необычных типах диалоговых окон, как всегда подробную информацию можно найти на страницах интерактивного справочного руководства.
Для получения вывода из диалогового окна любого типа, допускающего текстовый ввод или выбор, вы должны перехватить стандартный поток ошибок, как правило, направляя его во временный файл, который вы сможете обработать позже. Для получения ответа на вопросы типа "Да"/"Нет", просто проверьте код завершения, который, как и во всех соблюдающих приличия программах, в случае успеха возвращает 0 (т. е. выбор ответа "Да" (Yes)) и 1 в остальных случаях.
У всех типов диалоговых окон есть дополнительные управляющие параметры, такие как размер и контур отображаемого окна. В табл. 2.21 перечислены разные параметры, необходимые окнам каждого типа, а затем демонстрируется использование некоторых из них в командной строке. В заключение вы увидите простой пример, объединяющий в одной программе несколько диалоговых окон разных типов.
Таблица 2.21
–checklist | text height width list-height [tag text status] ... |
–infobox | text height width |
–inputbox | text height width [initial string] |
–menu | text height width menu-height [tag item ] ... |
–msgbox | text height width |
–radiolist | text height width list-height [tag text status] ... |
–textbox | filename height width |
–yesno | text height width |
Помимо параметров диалоговые окна всех типов принимают ряд опций. Мы не будем перечислять все, а упомянем лишь две из них: –title
, позволяющую задать заголовок окна, и -clear
, применяемую по прямому назначению, т.е. для очистки экрана. Полный список опций см. на страницах интерактивного справочного руководства.
Выполните упражнения 2.21 и 2.22.
Упражнение 2.21. Применение утилиты dialog
Давайте сразу перейдем к красивому сложному примеру. Если вы поймете его, все остальные покажутся легкими! В этом примере вы создадите диалоговое окно со списком флажков, с заголовком Check me (Поставь галочку) и пояснительной надписью Pick Numbers (Выбери номера). Окно с флажками будет высотой 15 строк и шириной 25 символов, и каждый флажок будет занимать 3 символа по высоте. И последнее, но не по степени важности, вы перечислите отображаемые элементы вместе с принятой по умолчанию установкой или сбросом (on/off) флажка.
dialog –title «Check me» –checklist «Pick Numbers» 15 25 3 1 «one» «off» 2 «two» «on» 3 «three» «off»
Полученный результат показан на рис. 2.4.
Как это работает
В этом примере параметр –checklist
указывает на то, что вы собираетесь создать диалоговое окно с флажками. Вы используете опцию –title
для задания заголовка "Check me
", следующий параметр – пояснительная надпись "Pick Numbers
".
Далее вы переходите к указанию размеров диалогового окна. Оно будет высотой 15 строк и шириной 25 символов и 3 строки отводятся для меню. Это не самый удачный выбор размеров, но он позволит вам увидеть, как размещаются элементы.
Опции выглядят несколько мудрено, но вам следует запомнить только то, что у каждого элемента списка есть три значения:
□ номер в списке;
□ текст;
□ состояние.
Рис. 2.4
У первого элемента номер 1, отображается текст «one» (один) и выбрано состояние «off» (сброшен). Далее вы переходите ко второму элементу с номером 2, текстом «two» и состоянием «on» (установлен). Так продолжается до тех пор, пока вы не опишите все элементы списка.
Легко, не правда ли? Теперь попробуйте ввести несколько вариантов в командной строке и убедитесь, насколько эту утилиту легко применять. Для того чтобы включить этот пример в программу, вы должны иметь доступ к результатам пользовательского ввода. Это совсем просто: перенаправьте стандартный поток ошибок в текстовый ввод или проверьте переменную окружения $?
, которая, как вы помните, не что иное, как код завершения предыдущей команды.
Упражнение 2.22. Более сложная программа, использующая утилиту dialog
Давайте рассмотрим простую программу questions, которая принимает к сведению пользовательские ответы.
1. Начните с простого диалогового окна, сообщающего пользователю о происходящем. Вам не нужно получать результат или какой бы то ни было ввод пользователя, поэтому все просто и ясно:
#!/bin/sh
# Задайте несколько вопросов и получите ответ
dialog –title «Questionnaire» –msgbox «Welcome to my simple survey» 9 18
2. Спросите пользователя с помощью простого диалогового окна с кнопками типа Yes/No, хочет ли он продолжать. Воспользуйтесь переменной окружения $?
для того, чтобы выяснить, выбрал пользователь ответ Yes (код завершения 0) или No. Если он не хочет двигаться дальше, используйте простое информационное окно, не требующее никакого пользовательского ввода для своего завершения.
dialog –title «Confirm» –yesno «Are you willing to take part?» 9 18
if [ $? != 0 ]; then
dialog –infobox «Thank you anyway» 5 20 sleep 2
dialog –clear exit 0
fi
3. Спросите у пользователя его имя с помощью диалогового окна ввода. Перенаправьте стандартный поток ошибок во временный файл _1.txt, который затем вы сможете обработать в переменной QNAME
.
dialog –title «Questionnaire» –inputbox «Please enter your name» 9 30 2>_1.txt
Q_NAME=$(cat _1.txt)
4. Здесь у вас появляется меню из четырех пунктов. И снова вы перенаправляете стандартный поток ошибок и загружаете его в переменную.
dialog –menu «$Q_NAME, what music do you like best?» 15 30 4 1 «Classical» 2 «Jazz» 3 «Country» 4 «Other» 2>_1.txt
Q_MUSIC=$(cat _1.txt)
5. Номер, выбранный пользователем, будет запоминаться во временном файле _1.txt, который перехватывается переменной Q_MUSIC
, поэтому вы сможете проверить результат.
if [ «$Q_MUSIC» = "1" ]; then
dialog –title «Likes Classical» –msgbox «Good choice!» 12 25
else
dialog –title «Doesn't like Classical» –msgbox «Shame» 12 25
fi
В заключение очистите последнее диалоговое окно и завершите программу.
sleep 2
dialog –clear
exit 0
На рис. 2.5 показан результат.
Как это работает
В данном примере вы соединяете команду dialog
и простой программный код на языке командной оболочки для того, чтобы показать, как можно создавать простые программы с графическим пользовательским интерфейсом, используя только сценарий командной оболочки. Вы начинаете с обычного экрана-приветствия, а затем с помощью простого диалогового окна с кнопками типа Yes/No спрашиваете пользователя о его желании участвовать в опросе. Вы используете переменную $?
для проверки ответа пользователя. Если он согласен, вы запрашиваете его имя, сохраняете его в переменной Q_NAME
и выясняете с помощью диалогового окна-меню, какой музыкальный стиль он любит. Сохранив числовой вывод в переменной Q_MUSIC
, вы сможете увидеть, что ответил пользователь, и отреагировать соответственно.
Рис. 2.5
Рис. 2.6
Если вы применяете графический пользовательский интерфейс (GUI) на базе графической среды GNOME и в данный момент запустили в нем сеанс работы с терминалом, на месте команды dialog
можно использовать команду gdialog
. У обеих команд одинаковые параметры, поэтому вы сможете воспользоваться тем же программным кодом, не считая замены запускаемой вами команды dialog
командой gdialog
. На рис. 2.6 показано, как выглядит этот сценарий в дистрибутиве Ubuntu, когда применяется команда gdialog
.
Это очень лёгкий способ формирования из сценария удачного графического пользовательского интерфейса.
Соединяем все вместе
Теперь, когда вы познакомились с основными функциональными возможностями командной оболочки как языка программирования, пора написать программу, в которой используется кое-что из того, чему вы научились.
На протяжении всей книги вы будете пытаться разработать приложение управления базой данных компакт-дисков для того, чтобы продемонстрировать приемы и методы, которые вы только что освоили. Вы начинаете со сценария командной оболочки, но очень скоро вы будете делать то же самое на языке программирования С, добавлять базу данных и т.д.
ТребованияПредположим, что у вас есть разнообразная коллекция компакт-дисков. Для того чтобы облегчить себе жизнь, вы собираетесь разработать и реализовать программу управления компакт-дисками. Электронный каталог представляется идеальным проектом для реализации, когда вы учитесь программированию в ОС Linux.
Вначале вы хотите, по меньшей мере, хранить некоторые основные сведения о каждом компакт-диске, такие как название, музыкальный стиль и исполнитель или композитор. Возможно, вам также хотелось бы запоминать некоторую простую информацию о дорожках. Вы хотите иметь возможность находить любое музыкальное произведение из хранящихся на компакт-диске, но не какие-то подробности, касающиеся дорожек. Для придания законченности мини-приложению вам хотелось бы иметь возможность в самом приложении вводить, обновлять и удалять любую часть информации.
ПроектированиеПоиск и отображение данных – заставляют предполагать, что адекватным решением будет простое меню. Все данные, которые следует хранить, – текстовые, и, полагая, что ваша коллекция компакт-дисков не слишком велика, для управления ею не потребуется сложная база данных, подойдут простые текстовые файлы. Сохранение информации в текстовых файлах позволит сделать приложение простым, и если ваши требования изменятся, всегда легче манипулировать текстовыми файлами, чем какими бы то ни было еще. И, в крайнем случае, вы сможете применить редактор для ввода и удаления данных вручную, а не писать для этого специальную программу.
Необходимо принять важное проектное решение, касающееся способа хранения данных. Достаточно одного файла? Если да, то какой у него должен быть формат? Большая часть информации, которую вы собираетесь хранить, за исключением данных о дорожках, вводится однократно для каждого компакт-диска (мы пока оставим в стороне вариант наличия на одном CD произведений разных композиторов и исполнителей). И практически на всех компакт-дисках много дорожек.
Нужно ли ограничить количество дорожек на одном компакт-диске, которые можно хранить? Это ограничение кажется лишним, поэтому сразу отбросим его.
Если вы допускаете, что на компакт-диске может быть разное количество дорожек, у вас есть три варианта:
□ использовать один файл с одной строкой для "заголовочной" типовой информации и n строк для сведений о дорожках на каждом компакт-диске;
□ поместить всю информацию о каждом компакт-диске в одну строку, разрешая ей продолжаться то тех пор, пока вся информация о дорожках диска не будет сохранена;
□ отделить заголовочную информацию от данных о дорожках и для каждого типа информации использовать отдельный файл.
Только третий вариант позволит нам легко изменять формат файлов, что потребуется, если вы когда-либо захотите превратить вашу базу данных в реляционную (более подробную информацию об этом см. в главе 7), поэтому выберем этот вариант.
Далее нужно решить, какие данные помещать в файлы.
Сначала вы выбираете для заголовка каждого компакт-диска хранение следующей информации:
□ номер компакт-диска в каталоге;
□ название;
□ музыкальный стиль (классика, рок, поп, джаз и т.д.);
□ композитор или исполнитель.
О дорожках вы будете хранить две характеристики:
□ номер дорожки;
□ ее название.
Для объединения двух файлов вы должны сопоставить данные о дорожках с остальной информацией о компакт-диске. Для этого будет использоваться номер компакт-диска в каталоге. Поскольку он уникален для каждого диска, он будет появляться однократно в файле с заголовками и один раз для каждой дорожки в файле с данными о дорожках.
В табл. 2.22 показан пример файла с заголовочными данными, а соответствующий ему файл с данными о дорожках может выглядеть так, как представлено в табл. 2.23.
Таблица 2.22
CD123 | Cool sax | Jazz | Bix |
CD234 | Classic violin | Classical | Bach |
CD345 | Hits99 | Pop | Various |
Таблица 2.23
CD123 | 1 | Some jazz |
CD123 | 2 | More jazz |
CD234 | 1 | Sonata in D minor |
CD345 | 1 | Dizzy |
Два файла объединены общим полем Catalog
(Каталог). Следует помнить о том, что обычно на одну строку файла с заголовочной информацией приходится много строк в файле с данными о дорожках.
Последнее, что мы должны решить, – способ разделения элементов данных. Поля фиксированной ширины, обычные в реляционных базах, – не всегда самый удобный вариант. Другой распространенный способ, применяемый в данном примере, – запятая (т. е. файл со значениями, разделенными запятыми, или CSV-файл).
В упражнении 2.23 только для того, чтобы вы окончательно не запутались, применяются следующие функции:
□ get_return()
;
□ get_confirm()
;
□ set_menu_choice()
;
□ insert_title()
;
□ insert_track()
;
□ add_record_tracks()
;
□ add_records()
;
□ find_cd()
;
□ update_cd()
;
□ count_cds()
;
□ remove_records()
;
□ list_tracks()
.
Упражнение 2.23. Приложение для работы с коллекцией компакт-дисков
1. Сначала в примере сценария как всегда стоит строка, обеспечивающая его выполнение как сценария командной оболочки, за которой следует некоторая информация об авторских правах:
#!/bin/bash
# Очень простой пример сценария командной оболочки для управления
# коллекцией компакт-дисков.
# Copyright (С) 1996-2007 Wiley Publishing Inc.
# Это свободно распространяемое программное обеспечение;
# вы можете распространять эту программу и/или изменять ее
# в соответствии с положениями GNU General Public License,
# документа, опубликованного фондом Free Software Foundation;
# либо версии 2 этой лицензии или (по вашему выбору)
# любой более свежей версии.
# Эта программа распространяется в надежде на ее полезность,
# но WITHOUT ANY WARRANTY, (без каких-либо гарантий);
# даже без предполагаемой гарантии MERCHANTABILITY
# or FITNESS FOR A PARTICULAR PURPOSE (годности
# ее для продажи или применения для определенной цели).
# Более подробную информацию см. в GNU General Public License.
# Вы должны были получить копию GNU General Public License
# вместе с этой программой;
# если нет, пишите в организацию Free Software Foundation,
# Inc. no адресу: 675 Mass Ave, Cambridge, MA 02139, USA.
2. Теперь убедитесь, что установлены некоторые глобальные переменные, которые будут использоваться во всем сценарии. Задайте заголовочный файл, файл с данными о дорожках и временный файл и перехватите нажатие комбинации клавиш
menu_choice=""
current cd=""
title_file="title.cdb"
tracks_file="tracks.cdb"
temp_file=/tmp/cdb.$$
trap 'rm -f $temp_file' EXIT
3. Определите ваши функции так, чтобы сценарий, выполняясь с первой строки, мог найти все определения функций до того, как вы попытаетесь вызвать любую из них в первый раз. Для того чтобы не повторять один и тот же программный код в нескольких местах, сначала вставьте две функции, служащие простыми утилитами:
get_return() (
echo -е «Press return с»
read x
return 0
}
get_confirm() (
echo -e «Are you sure? c»
while true do
read x
case «$x» in
y | yes | Y | Yes | YES )
return 0;;
n | no | N | No | NO )
echo
echo «Cancelled»
return 1;;
*)
echo «Please enter yes or no» ;;
esac
done
}
4. Теперь вы дошли до основной, формирующей меню функции set_menu_choice
. Содержимое меню изменяется динамически, добавляя дополнительные пункты при выборе компакт-диска.
set_menu_choice() {
clear
echo «Options :-»
echo
echo « a) Add new CD»
echo « f) Find CD»
echo « c) Count the CDs and tracks in the catalog»
if [ «$cdcatnum» != "" ]; then
echo « 1) List tracks on $cdtitle»
echo « r) Remove $cdtitle»
echo « u) Update track information for $cdtitle»
fi
echo « q) Quit» echo
echo -e «Please enter choice then press return c»
read menu_choice
return
}
Примечание
Имейте в виду, что команда
echo -е
не переносится в некоторые командные оболочки.
5. Далее идут две очень короткие функции, insert_title
и insert_track
, для пополнения файлов базы данных. Несмотря на то, что некоторые программисты ненавидят однострочные функции вроде этих, они помогают сделать понятнее другие функции.
За ними следует более длинная функция add_record_track
, использующая эти функции. В данной функции применяется проверка на соответствие шаблону, чтобы исключить ввод запятых (поскольку мы. используем запятые для разделения полей), и арифметические операции для увеличения номера текущей дорожки на 1, когда вводятся данные о новой дорожке.
insert_title() {
echo $* >> $title_file
return
}
insert_track() {
echo $* >> $tracks_file
return
}
add_record_tracks() {
echo «Enter track information for this CD»
echo «When no more tracks enter q»
cdtrack=1
cdttitle=""
while [ «$cdttitle» != "q" ]
do
echo -e «Track $cdtrack, track title? c»
read tmp
cdttitle=${tmp%%, *}
if [ «$tmp» != «$cdttitle» ]; then
echo «Sorry, no commas allowed»
continue
fi
if [ -n «$cdttitle» ] ; then
if [ «$cdttitle» ! = "q" ]; then
insert_track $cdcatnum, $cdtrack, $cdttitle
fi
else
cdtrack=$((cdtrack-1))
fi
cdtrack=$((cdtrack+1))
done
}
6. Функция add_records
позволяет вводить основную информацию о новом компакт-диске.
add_records() {
# Подсказка для начала ввода информации
echo -е «Enter catalog name с»
read tmp
cdcatnum=${tmp%%, *}
echo -e «Enter title c»
read tmp
cdtitle=${tmp%%, *}
echo -e «Enter type c»
read tmp
cdtype=${tmp%%, *}
echo -e «Enter artist/composer c»
read tmp
cdac=${tmp%%, *}
# Проверяет, хочет ли пользователь ввести информацию
echo About to add new entry
echo «$cdcatnum $cdtitle $cdtype $cdac»
# Если получено подтверждение, добавляет данные в конец файла.
# с заголовками
if get_confirm ; then
insert_title $cdcatnum, $cdtitle, $cdtype, $cdac
add_record_tracks
else
remove_records
fi
return
}
7. Функция find_cd
с помощью команды grep
ищет текст с названием компакт-диска в файле с заголовочной информацией. Вам нужно знать, сколько раз была найдена строка, а команда grep только вернет значение, указывающее на то, что строка не была найдена или была найдена многократно. Для решения этой проблемы сохраните вывод в файл, отводящий по одной строке на каждое найденное совпадение, а затем сосчитайте количество строк в файле.
У команды счетчика слов, wc
, в выводе есть пробельный символ, разделяющий количества строк, слов и символов в файле. Используйте синтаксическую запись $(wc -l $temp_file)
для извлечения первого параметра в выводе и переноса его в переменную linesfound
. Если бы вам был нужен другой следующий далее параметр, нужно было бы воспользоваться командой set
для установки значений переменных-параметров оболочки из вывода команды.
Изменив значение переменной IFS
(Internal Field Separator, внутренний разделитель полей) на запятую, вы сможете разделить поля, разграниченные запятыми. Альтернативный вариант – применить команду cut
.
find_сd() {
if [ «$1» = "n" ]; then
asklist=n
else
asklist=y
fi
cdcatnum=""
echo -e «Enter a string to search for in the CD titles c»
read searchstr
if [ «$searchstr» = "" ]; then
return 0
fi
grep «$searchstr» $title_file > $temp_file
set $(wc -l $temp_file)
linesfound=$1
case «$linesfound» in
0)
echo «Sorry, nothing found»
get_return
return 0 ;;
1) ;;
2)
echo «Sorry, not unique.»
echo «Found the following»
cat $temp_file
get_return
return 0
esac
IFS=", "
read cdcatnum cdtitle cdtype cdac < $temp_file
IFS=" "
if [ -z «$cdcatnum» ]; then
echo «Sorry, could not extract catalog field from $temp_file»
get_return
return 0
fi
echo
echo Catalog number: $cdcatnum echo Title: $cdtitle
echo Type: $cdtype
echo Artist/Composer: $cdac
echo
get_return
if [ «$asklist» = "y" ]; then
echo -e «View tracks for this CD? c»
read x
if [ «$x» = "y" ]; then
echo
list_tracks
echo
fi
fi
return 1
}
8. Функция update_cd
позволит вам повторно ввести сведения о компакт-диске. Учтите, что вы ищите (с помощью команды grep
) строки, начинающиеся (^
) с подстроки $cdcatnum
, за которой следует ", "
и должны заключить подстановку значения $cdcatnum
в {}
. Таким образом, вы сможете найти запятую без специального пробельного символа между ней и номером в каталоге. Эта функция также использует {}
для образования блока из нескольких операторов, которые должны выполняться, если функция get_confirm
вернет значение true.
update_cd() {
if [ -z «$cdcatnum» ]; then
echo «You must select a CD first»
find_cd n
fi
if [ -n «$cdcatnum» ]; then
echo «Current tracks are :-»
list_tracks
echo
echo «This will re-enter the tracks for $cdtitle»
get_confirm && {
grep -v "^${cdcatnum}, " $tracks_file > $temp_file
mv $temp_file $tracks_file
echo
add_record_tracks
}
fi
return
}
9. Функция count_cds
дает возможность быстро пересчитать содержимое базы данных.
count_cds() {
set $(wc -l $title_file)
num_titles=$1
set $(wc -l $tracks_file)
num_tracks=$1
echo found $num_titles CDs, with a total of $num_tracks tracks
get_return
return
}
10. Функция remove_records
удаляет элементы из файлов базы данных с помощью команды grep -v
, удаляющей все совпадающие строки. Учтите, что нужно применять временный файл.
Если вы попытаетесь применить команду:
grep -v «^$cdcatnum» > $title_file
файл $title_file
станет пустым благодаря перенаправлению вывода >
до того, как команда grep
выполнится, поэтому она будет читать уже пустой файл.