ПРИЕМЫ ПРОФЕССИОНАЛЬНОЙ РАБОТЫ В UNIX ГЛАВА 5 УПРАВЛЕНИЕ ЛИЧНОЙ ИНФОРМАЦИЕЙ I: УПРАВЛЕНИЕ ВРЕМЕНЕМ И ДЕЛОПРОИЗВОДСТВОМ УПРАВЛЕНИЕ ВРЕМЕНЕМ at выполнение задач в указанное время b порожденный shell фоновых задач greet своевременное приветствие с терминала lastlog сообщение времени последней регистрации timelog учет и статистика сеансов работы today печать календаря с отмеченной текущей датой УПРАВЛЕНИЕ ДЕЛОПРОИЗВОДСТВОМ jargon генератор технических терминов phone база данных с телефонными номерами office делопроизводитель УПРАВЛЕНИЕ ЛИЧНОЙ ИНФОРМАЦИЕЙ I: УПРАВЛЕНИЕ ВРЕМЕНЕМ И ДЕЛОПРОИЗВОДСТВОМ ВВЕДЕНИЕ Мы уже многое знаем о файлах и о том, как управлять файловой структурой. Пора рассмотреть, как мы можем использовать систему UNIX для управления множеством задач, которые составляют наш рабочий день и держат нас в курсе того, что делают другие пользователи. Термин "уп- равление личной информацией" (personal management) подразумевает, что вы хотите создать свою собственную ПЕРСОНАЛЬНУЮ рабочую среду и инструментальные средства. Мы предлагаем вам пакет программ, которые вы можете приспособить к вашим требованиям. Фактически мы в этой и следующей главе представляем четыре отдельных набора программ, каждый из которых посвящен определенному аспекту управления личной информаци- ей. Средства управления временем помогают нам спланировать выполнение задач компьютером, а также контролировать наше личное время. Управле- ние делопроизводством имеет дело с хранением и извлечением информации, а также с организацией доступа к различным функциям системы UNIX посредством простого в использовании интерфейса в виде меню. Для каждой из этих областей деятельности мы даем ее обзор, а за- тем представляем соответствующую группу средств. УПРАВЛЕНИЕ ВРЕМЕНЕМ Поскольку система UNIX имеет встроенные функции поддержки времени и часы, она может следить за временем. Объединение функций поддержки времени с возможностью автоматического запуска группы команд означает, что мы можем настроить их так, чтобы компьютер выполнял многие наши рутинные работы, связанные со временем. Мы также можем использовать компьютер для отслеживания нашего собственного времени. В данном разделе представлены инструментальные средства at, b, greet, lastlog, timelog и today. Командный файл at дает нам возможность сказать машине о том, что в указанное время необходимо сделать то-то и то-то (вывести на экран сообщение или выполнить какие-то другие команды). Задача запускается в фоновом режиме, так что мы можем продолжать другую работу, а фоновая задача выполнится автоматически в указанное время. Эта задача может состоять из любых разрешенных в UNIX команд, поэтому ее возможности очень гибкие. Мы просто предлагаем некоторые идеи, связанные с ее использованием. Вторым средством является командный файл b. Это обработчик фоно- вых задач. Очень часто при порождении фоновых процессов мы не можем узнать, когда они закончились. Для того, чтобы это определить, нам не- обходимо вручную просмотреть таблицу процессов или найти какой-то иной признак того, что данная работа завершена. Командный файл b запускает задачу, управляет операциями ввода-вывода и затем сообщает нам о том, что задача завершена. Командный файл greet показывает, каким образом переводить внут- реннее время компьютера в более понятные пользователю категории. Он различает три перида суток (утро, день и вечер) и реагирует на них соответствующими сообщениями. Это довольно просто, но обеспечивает неплохое основание для подхода к решению других проблем, связанных со временем. Далее мы представляем два средства, которые образуют базис систе- мы управления временем. При выполнении множества работ нам необходимо подсчитать время, которое мы потратили на данный проект, чтобы мы мог- ли выставить нашему клиенту соответствующий счет. Командный файл lastlog запускается автоматически, когда вы регистрируетесь в системе. Поддерживается база данных, в которую каждый раз записывается время вашей регистрации для последующего анализа или хранения записей. С этим инструментальным средством соседствует командный файл timelog. Это утилита, которая выполняет подсчет времени. Она может следить за общим временем, затраченным на любой указанный проект. За- тем можно сгенерировать статистику, которая показывает, когда и сколь- ко времени вы работали над каждым проектом. Последнее средство, относящееся ко времени - это командный файл today. Это утилита, которая изменяет вид выходных данных команды UNIX cal. Она печатает обычный календарь, только текущая дата выводится в инверсном виде. Это очень наглядно. Вы можете развить этот инструмент для того, чтобы отмечать праздники или другие особые дни. ---------------------------------------------------- ИМЯ: at ---------------------------------------------------- at - выполнить команду или файл в указанное время НАЗНАЧЕНИЕ Переводит любую командную строку в фоновый режим и выполняет ее в заданное время. ФОРМАТ ВЫЗОВА at hr:min cmd [;cmd ...] ПРИМЕР ВЫЗОВА at 12:00 echo "time for lunch!" В двенадцать часов дня выводит сообщение на экран терминала. ТЕКСТ ПРОГРАММЫ at 1 : 2 # @(#) tree v1.0 Execute command line at specific time Author: Russ Sage 2а Выполнить командную строку в указанное время 3 4 if [ $# -lt 2 ] 5 then echo "at: wrong arg count" >&2 6 echo "usage: at hr:min cmd [;cmd ...]" >&2 7 exit 1 8 fi 9 10 ITS=$1; shift 11 12 while : 13 do 14 TIME=`date | cut -c12-16` 15 16 if [ "$ITS" = "$TIME" ] 17 then eval $@ 18 exit 0 19 else sleep 35 20 fi 21 done & ПЕРЕМЕННЫЕ СРЕДЫ ВЫПОЛНЕНИЯ ITS Время, в которое следует выполнить указанные команды TIME Текущее время в системе ОПИСАНИЕ ЗАЧЕМ НАМ НУЖЕН at? На протяжении рабочего дня мы выполняем много небольших работ, которые нужно делать через различные интервалы времени. Некоторые вещи просто должны быть сделаны один раз в любое время, тогда как другие должны делаться в определенное время каждый день. Например, вам может понадобиться запускать процедуру копирования файлов каждую ночь, вхо- дить в другую систему раз в день и проверять почту или сообщения поль- зователей сети по заданной теме раз в несколько дней. Командный файл at предоставляет механизм для выполнения задач, связанных со временем. Мы можем сказать системе, что и когда мы хотим сделать. Задача остается "спящей" в фоновом режиме до назначенного времени. Это дает нам возможность превратить компьютер в будильник, секретаря, администратора встреч и т.д. Данная концепция не нова и уже существует в системе Berkeley UNIX под тем же именем. Она реализована также в последних версиях System V. Почему же тогда мы представляем здесь нашу собственную версию? Одна из причин в том, что многие из вас имеют более ранние версии UNIX, в которых это средство отсутствует. Но важнее, видимо, другое - наша цель не в том, чтобы сделать существующие команды at устаревшими, а в показе того, как легко отслеживать время и реализовывать обработ- ку, связанную со временем. Имея нашу собственную команду at, мы можем настроить ее по своему вкусу и изменить ее, когда необходимо. Команда at, представленная здесь, фактически более гибкая, чем at в системе Berkeley, хотя в первой отсутствуют некоторые особенности второй. Она более гибкая потому, что вы можете поместить настоящие команды в фоно- вую задачу at, в то время как для at в системе Berkeley вы должны использовать имя командного файла интерпретатора shell. Метод Berkeley запрещает вам вызывать исполняемые модули непосредственно в командной строке, а наша at - нет. (Конечно, вы можете с таким же успехом использовать командные файлы интерпретатора shell, если вам это необ- ходимо.) ЧТО ДЕЛАЕТ at? Команда at дает нам возможность собирать несколько команд в одно целое и впоследствии запускать их. Когда они выполняются, их вывод мо- жет либо идти на экран, либо перенаправляться в определенный файл. Командная строка принимает два параметра: время выполнения и ко- мандную строку, которую следует выполнить. Время выражено в формате час:минута. Час должен быть указан строкой из двух цифр (в диапазоне от 0 до 23 часов), так как его использует команда date. Использование того же стандарта, что и в команде date, значительно упрощает команду at. В качестве второго параметра может быть любая команда, которую обычно можно ввести в командной строке интерпретатора shell. Можно также использовать конвейеры, составные команды и переназначения. Нет ограничений на то, какая команда может быть выполнена. Команда at мо- жет запустить обычный исполняемый модуль UNIX или ваш собственный ко- мандый файл. Выход at по умолчанию направляется в стандартный вывод. Стандарт- ным выводом в данном случае является экран терминала. Команда at в системе Berkeley не имеет вывода по умолчанию, что несколько затрудня- ет получение результата и отправку его на экран. Команда at всегда запускается как фоновая задача. Нецелесообразно запускать ее в приоритетном режиме, где она блокирует терминал на все время своего выполнения. Пребывая в фоновом режиме, at освобождает ресурсы, но все же работает для вас. Между прочим, отметим, что когда процессы ставятся в фоновый режим изнутри командного файла, идентифи- катор процесса не печатается на экран, как это происходит, когда про- цессы запускаются в фоновом режиме с клавиатуры. Порождение большого количества фоновых процессов может иметь от- рицательный эффект для системы. Каждая фоновая задача - это цикл while интерпретатора shell, который работает очень медленно. Когда много фо- новых процессов, мало времени центрального процессора остается на дру- гие цели. В результате производительность системы ухудшается. В боль- ших системах это, вероятно, не проблема, если только система не загру- жена множеством пользователей, но вы должны использовать это средство с осторожностью. Отметим, что формат час:минута годится только для одного полного дня. Данная программа at задумана как ежедневная и не имеет средств запуска в определенный день или месяц, хотя вы можете легко расширить ее, как только поймете, как читается и используется информация о вре- мени. ПРИМЕРЫ 1. $ at 11:45 echo ^G^G It's almost lunch time Без пятнадцати минут двенадцать дважды выдается звуковой сигнал (control-G) и выводится сообщение о ленче. 2. $ at 10:45 "if [ -s $MAIL ]; then echo ^G You have mail; fi" Без пятнадцати одиннадцать проверяется, существует ли мой почто- вый файл и есть ли в нем хотя бы один символ ($MAIL есть /usr/spool/mail/russ). Если это так, выдается звуковой сигнал и сооб- щение о том, что у меня есть почта. 3. $ at 17:00 "c; date; banner ' time to' ' go home'" В пять часов вечера очищается экран (с помощью команды c, описан- ной далее в данной книге), печатается дата и выводится крупными буква- ми на весь экран сообщение "time to go home" ("пора домой"). С помощью апострофов в командной строке banner мы можем добиться вывода символа возврата каретки, чтобы разместить каждый набор слов в отдельной стро- ке. Если какая-либо из этих команд не срабатывает (например, не найде- на команда c), то и весь фоновый процесс оканчивается неудачей. ПОЯСНЕНИЯ Прежде всего at проверяет, правильно ли она была вызвана. Строки 4-8 делают проверку ошибок. В командной строке должны присутствовать по крайней мере два параметра: время и команда. Если это так, то счет- чик позиционных параметров равен 2. Если этот счетчик меньше 2, прои- зошла ошибка. В стандартный файл ошибок посылаются сообщения об ошибке с помощью переадресации в файловый дескриптор 2. Переменная интерпретатора shell ITS инициализируется в строке 10. В ней устанавливается значение первого позиционного параметра ($1), которым является час:минута. Как только мы занесли это значение в пе- ременную, оно больше не нужно нам в командной строке. Команда shift удаляет $1 из командной строки. Теперь командная строка состоит из вы- зывающей команды $0 (т.е. самой at) и остатка строки ($@ или $*). Вы- зывающая команда не вычисляется как часть остатка строки, поэтому вам не нужно заботиться об аргументе $0. Далее at переходит к вечному циклу while в строках 12-21. Вечным этот цикл делает команда : (двоеточие). Это встроенная команда интерп- ретатора shell, которая ничего не делает кроме того, что всегда возв- ращает успешный статус выхода, заставляя тем самым цикл продолжаться. Команда true интерпретатора shell очень похожа и делает программу бо- лее наглядной. Мы же используем : вместо true, чтобы сократить издерж- ки на порождение процесса для каждой итерации цикла. Команда : встрое- на в сам shell. True, напротив, является внешней командой в каталоге bin (так же, как ls), она должна быть найдена по файловому пути, вы- полниться и вернуть значение. Это занимает гораздо больше процессорно- го времени. На каждой итерации цикла текущее время сверяется с назначенным временем, переданным из командной строки. Текущее время извлекается из команды date в строке 14. Обычно date выдает результат в таком форма- те: ---------------------------- | | Mon Mar 31 06:54:25 PST 1986 | | Поскольку это строка фиксированного размера, мы можем посчитать номера позиций, в которых размещены час и минута. Данные час:минута находятся в позициях 12-16. Для получения этих символов мы запускаем команду date, пропускаем ее результат по конвейеру через cut и выреза- ем нужные позиции. Весь результат присваивается переменной TIME. Заме- тим, что поле секунд не используется. Наименьшая единица времени в этой программе - минута. Все волшебство данной команды заключено в строках 16-20. Если время, указанное в командной строке, равно текущему времени (строка 16), вычислить и выполнить остальные аргументы командной строки (стро- ка 17), затем выйти с успешным нулевым значением (строка 18). Если время не совпало, немного поспать (строка 19) и повторить все сначала. Символ & в конце цикла в строке 21 превращает весь цикл while в фоновый процесс. Как мы можем убедиться, что shell выполняет все свои команды в фоновом режиме и никакие из них не выполняет в оперативном режиме? В действительности мы не можем этого сделать. Мы должны пола- гать, что shell так работает. Поскольку многое при программировании на shell делается исходя из опыта и интуиции, вам приходится испытывать многие вещи, чтобы увидеть, как они работают. Периодически shell пре- подносит сюрпризы и делает нечто совершенно неожиданное. ИССЛЕДОВАНИЯ Что бы случилось, если бы вы поставили задание at в фоновый ре- жим, а затем вышли из системы? Ответ зависит от того, с каким shell вы работаете. Если у вас Bourne shell, то ввод команды control-D при вы- ходе из системы прекращает выполнение всех ваших фоновых задач. Единственный способ оставить в живых фоновые задачи после выхода из системы - использовать команду nohup ("no hang up" - "не казнить"). Nohup обеспечивает, что все сигналы о прекращении процесса не достига- ют данного процесса. Не получая сигнал о прекращении выполнения, про- цесс думает, что вы все еще находитесь в системе. Синтаксис выглядит так: nohup at 13:00 echo "back from lunch yet?" Если вы запускаете Си-shell, все фоновые процессы продолжаются после вашего выхода из системы. Причина в том, что Си-shell переводит все свои фоновые задачи в состояние защиты от прекращения выполнения. Этот процесс автоматический, и его не нужно указывать явно. Вы, возможно, удивлены тем, что в строке 17 использована команда "eval $@". Это сформировалось методом проб и ошибок. На начальных эта- пах разработки at команда "$@" использовалась сама по себе. При са- мостоятельном применении эта команда означает "выполнить все позицион- ные параметры". Поскольку это была единственная команда, выполнялась вся строка позиционных параметров, после чего возникали проблемы. Использование переназначений и переменных интерпретатора shell сильно запутывало at. Для иллюстрации рассмотрим пару примеров. Если мы запускаем at с командной строкой at 09:30 echo $HOME то все вроде бы работает. Сначала раскрывается переменная $HOME, затем echo печатает ее значение - /usr/russ. Но если мы запускаем командную строку at 09:30 echo \$HOME то переменная не раскрывается и echo фактически печатает $HOME вместо значения переменной $HOME. Мы избежали этого просто с помощью команды eval для повторного вычисления командной строки перед ее выполнением. Существо проблемы в том, что вызывающий интерпретатор shell не раскры- вает значение переменной, поэтому мы заставляем выполняющийся shell повторно анализировать командную строку и вычислять все переменные. На этот раз значения переменных раскрываются, и мы получаем верный конеч- ный результат. МОДИФИКАЦИИ Возможно, вы захотите более подробно рассмотреть интерфейс со временем. В нынешнем состоянии at воспринимает только время в пределах от 0 до 23 часов в течение одного дня. Неплохим дополнением было бы заставить его различать время суток, т.е. 8:30 a.m. (до полудня) или 8:30 p.m. (после полудня). Было бы неплохо также иметь возможность сказать "через 10 минут сделать то-то и то-то". В этом случае команда могла бы иметь примерно такой вид: at -n 10 echo "do in now plus 10 minutes" где -n было бы текущим временем, а 10 добавлялось бы к нему. Другой очевидной областью модификации является наделение at воз- можностью запоминания периодов времени, превышающих сутки. Это может быть завтрашний день, определенный день или даже определенный месяц. Работа с определенным месяцем может быть не совсем реальной, поскольку командный файл, выполняемый в фоновом режиме в течение нескольких месяцев, потребует громадного количества процессорного времени, а так- же хранения постоянно возрастающего счетчика идентификаторов про- цессов, что даст, вероятно, пользователям искаженное представление об активности системы. По достижении максимального номера процесса, снова вернутся младшие номера, так что это не приведет к каким -либо серьез- ным последствиям. Решение вопроса о том, стоит ли такой ценой дости- гать вашей цели, зависит от того, считаете ли вы, что ваши требования излишне загружают систему. ------------------------------------------------------------- ИМЯ: b -------------------------------------------------------------- b Обработчик фоновых задач ФОРМАТ ВЫЗОВА b any_command_with_options_and_arguments (любая команда с опциями и аргументами) ПРИМЕР ВЫЗОВА b cg f.c Компилировать исходный файл в фоновом режиме, где cg - командная строка компилятора, описанная в главе 10. ТЕКСТ ПРОГРАММЫ b 1 : 2 # @(#) b v1.0 Background task handler Author: Russ Sage 2а Обработчик фоновых задач 3 4 ($@; echo "^G\ndone\n${PS1}\c") & ОПИСАНИЕ ЗАЧЕМ НАМ НУЖЕН b? Как вы видели в последнем разделе, Bourne shell дает возможность запускать задачи в фоновом режиме выполнения. Это делает символ &. Что же на самом деле происходит, когда мы запускаем что-нибудь в фоновом режиме? Порождается еще один shell, который должен выполнить свою собственную командную строку. После того, как все его команды выпол- нятся, он завершается. Вы можете определить фоновые задачи по резуль- тату работы команды ps. Эти задачи выглядят как интерпретаторы shell, запущенные с вашего терминала, однако их владельцем, или родительским процессом в действительности является команда init, а не ваш регистра- ционный shell (это справедливо только для shell, к которым применена команда nohup). Интерпретаторы shell, к которым не применялась nohup, принадлежат вашему регистрационному shell. Ниже приводится пример распечатки команды ps для фоновых задач. Командой для выполнения в фо- новом режиме была: while :;do date; done & Команда ps показывает мой регистрационный shell (PID=32), введен- ную мной командную строку для выполнения в фоновом режиме (PID=419) и shell, который выполняет цикл while (PID=449). ----------------------- | | UID PID PPID C STIME TTY TIME COMMAND | | root 0 0 0 Dec 31 ? 0:03 swapper | root 1 0 0 Dec 31 ? 0:02 /etc/init | russ 32 1 0 14:18:36 03 1:26 -shV | russ 419 32 0 15:30:31 03 0:02 -shV | russ 449 419 2 15:30:31 03 0:02 -shV | Ниже приведен листинг команды ps, который показывает фоновый shell, принадлежащий процессу init. Он был получен командой "b ps -ef", где b - утилита, которая будет рассмотрена далее. Как видите, последний процесс 471 есть фоновый shell, принадлежащий процессу 1, которым является init, а не мой регистрационный shell (PID=32). ------------------------- | | UID PID PPID C STIME TTY TIME COMMAND | root 0 0 1 Dec 31 ? 0:04 swapper | root 1 0 0 Dec 31 ? 0:02 /etc/init | russ 32 1 1 14:18:36 03 1:30 -shV | russ 472 471 5 15:46:46 03 0:12 ps -ef | russ 471 1 0 15:46:46 03 0:00 -shV | К чему все это приводит? Когда мы используем фоновые задачи, мы должны мириться с "неразборчивостью" при управлении асинхронными про- цессами. Каковы эти недостатки? Во-первых, мы никогда не знаем момента завершения фоновых задач. Единственный способ определить момент завершения таких задач - провер- ка результатов в каком-либо файле или некоторой работы, выполненной задачей, или использование команды ps и постоянное слежение за тем, когда процесс завершится. Такое слежение при помощи команды ps - не самый лучший способ, поскольку ps занимает много процессорного времени и очень медленно работает. Второй неаккуратный момент - это символ приглашения после выдачи на ваш экран результата из фоновой задачи. После того как выдан ре- зультат из фоновой задачи, ваш регистрационный shell ожидает ввода ко- манды, но приглашения может и не быть, поскольку оно было удалено с экрана некоторым другим сообщением. Вы можете ожидать приглашения це- лый день, но оно никогда не появится, поскольку оно уже было выведено на экран. Вы просто должны знать, что shell ждет вашу команду. Нам необходимо инструментальное средство, которое сообщает нам о завершении фоновой задачи, а также восстанавливает наш экран после вы- дачи на него каких-либо результатов. Можем ли мы сказать, выполняла ли вывод на экран фоновая задача или нет? Нет, поэтому мы должны жестко запрограммировать восстановление экрана в программе. ЧТО ДЕЛАЕТ b? Командный файл b - это механизм, который помогает в выполнении фоновых задач. Он запускает наши фоновые задачи. По завершении он отображает на экран слово "done" и затем повторно выводит символ-приг- лашение shell. Данное средство не имеет опций и проверки на наличие ошибок. Об- работчик фоновых задач фактически выполняет командную строку, которую мы ему передаем, и последующую обработку. Отметим, что для выдачи на экран вашего символа приглашения, вы должны экспортировать переменную PS1 из вашей текущей среды. Это может соблюдаться не на всех машинах, поскольку каждая система UNIX имеет свои особенности. В системе XENIX переменная PS1 не передается, возможно из-за того, что это shell, ко- торый вызывает другой shell в фоновом режиме. Если вы скажете "sh" в интерактивном режиме, он будет передан как приглашение. Система UNIX так прекрасна и удивительна! ПРИМЕРЫ 1. $ b ls -R .. Начиная с родительского каталога, рекурсивно составляется список всех файлов и выводится на экран. Обратите внимание, что при использо- вании фоновых задач вы не можете эффективно передать все это по конве- йеру команде more, поскольку обычным входным устройством для фоновых задач является /dev/null. Команда more не работает нормально, когда ее вызывают из фонового режима. Это также имеет смысл, поскольку вы могли бы иметь две задачи - одну в фоновом режиме, а другую в приоритетном - производящие беспорядок на экране. Фоновая команда more должна была бы сохранять в неприкосновенности то, что выводит на экран приоритетная команда. 2. $ b echo hello > z Файл z содержит не только слово "hello", но также и сообщение "done", поскольку переадресация в файл z выполняется во внешнем shell. Переадресация для подзадачи должна быть выполнена в круглых скобках программы b, а мы в данном случае не можем этого сделать. 3. $ b sleep 5; echo hello Эта командная строка не может быть выполнена, поскольку программа b воспринимает только команду sleep. Команда echo не помещается в фо- новый процесс и сразу же выполняется. 4. $ b "sleep 5; echo hello" Эту командную строку мы тоже не можем выполнить, поскольку эти две команды будут восприняты командным файлом b как одна. Затем коман- да sleep не выполнится, поскольку "5; echo hello" является недопусти- мым указанием временного периода для команды sleep. ПОЯСНЕНИЯ Обратите внимание, что в строке 4 вся структура команды заключена в круглые скобки, за которыми следует символ &. Круглые скобки переда- ют всю структуру подчиненному shell, который затем помещается в фоно- вый режим выполнения. Помещая все команды в один shell, мы гарантируем вывод на экран слова "done" после завершения последнего процесса. Данная командная строка выполняется с помощью символов $@. Это означает: "выполнить всю командную строку, расположенную справа". Поскольку выражение $@ выполняет само себя (т.е. не в операторе echo или в чем-либо подобном), то shell просто выполняет команды исходной командной строки. Это именно то, что мы хотим! Обратите внимание, что здесь нет никакого оператора eval. Поскольку то, что мы делаем, похоже на своего рода "командный интерпретатор строк" для их ввода и исполне- ния, вы могли бы подумать, что команда eval здесь необходима. По опыту мы знаем, что это не так. Похоже, что применение eval усложнит дело. Даже наш старый тест, использующий переменные среды выполнения, рабо- тает. По команде b echo $HOME на экран будет выдано сообщение /usr/russ Когда вся команда выполнится, подается звуковой сигнал и выво- дится сообщение, информирующее пользователя о том, что операция завер- шилась. Поскольку это сообщение накладывается на то, что было на экра- не, то переотображается начальный символ приглашения (PS1). Это делает нормальным вид экрана в том смысле, что символ приглашения shell сооб- щает об ожидании ввода. --------------------------------------------------------- ИМЯ: greet --------------------------------------------------------- greet Своевременное приветствие с терминала НАЗНАЧЕНИЕ Определение времени суток и печать приветствия и какого-либо сообщения на терминал в зависимости от времени дня. ФОРМАТ ВЫЗОВА greet ПРИМЕР ВЫЗОВА greet Вызывает командный файл greet, который определяет время и печатает соответствующее сообщение. ТЕКСТ ПРОГРАММЫ greet 1 : 2 # @(#) greet v1.0 Timely greeting from the terminal Author: Russ Sage 2а Своевременное приветствие с терминала 3 4 if [ `expr \`date +%H\` \< 12` = "1" ] 5 then echo "\nGood morning.\nWhat is the best use of your time right now?" 6 elif [ `expr \`date +%H\` \< 18` ="1" ] 7 then echo "\nGood afternoon.\nRemember, only handle a piece of paper once!" 8 else echo "\nGood evening.\nPlan for tomorrow today." 9 fi ОПИСАНИЕ ЗАЧЕМ НАМ НУЖЕН greet? Одним из замечательных преимуществ многопользовательских операци- онных систем является то, что они имеют хорошо развитую концепцию вре- мени. Обычно они содержат часы реального времени и некоторое программ- ное обеспечение, которое манипулирует с ними. Однако всегда есть место для дополнительного программного обеспечения, работающего со временем. Такие средства могут быть написаны как на языке Си, так и на shell-языке. Как мы извлекаем и выделяем время с помощью командного файла ин- терпретатора shell? Доступно много способов, но стандартная команда UNIX date, видимо, является наилучшим способом. В случае языка Си вы должны программно управлять преобразованием времени и временными зона- ми. Команда date делает это для вас. Важна также единица времени. Должны ли мы различать секунды, ми- нуты, часы, дни или недели? Это все зависит от требуемого приложения. В нашем простом примере мы различаем только три части суток: утро, день и вечер. Мы определили эти периоды так: с полуночи до полудня, от полудня до шести часов и от шести часов до полуночи соответственно. ЧТО ДЕЛАЕТ greet? Greet - это утилита, которая приветствует пользователя различными сообщениями в зависимости от времени суток. Выводимые сообщения не так важны. Они в основном использованы как примеры, показывающие, как мо- гут быть выполнены какие-то команды. Если вы работаете в одиночестве и хотели бы поболтать, эти сообщения могли бы читаться периодически из соответствующих файлов для создания иллюзии автоматической письменной болтовни в зависимости от времени суток. Действительной же целью является создание каркаса программы, ко- торая может переключаться в зависимости от параметров времени. Путем расширения концепции времени вы можете создать другие утилиты, которые знают, когда им работать (в какой промежуток времени) и могут вести себя иначе в соответствии со временем. Greet не требует ничего в командной строке. Не выполняется ника- кой проверки на наличие ошибок, поэтому и нет в программе синтакси- ческой подсказки. Выход команды greet может быть переадресован в файл или передан по конвейеру другому процессу. ПРИМЕРЫ 1. $ if greet | fgrep 'morn' > /dev/null > then morning_routine > fi Выполняется greet. Стандартный вывод greet по конвейеру переда- ется на стандартный ввод fgrep. Производится поиск символьной строки "morn". Весь выход переадресовывается в никуда, так что он не засоряет экран. Если выходной статус команды fgrep равен нулю (она нашла нужную строку), выполняется файл morning_routine. 2. $ at 10:30 greet; at 13:50 greet Вы могли бы вставить это в ваш .profile. Два процесса at будут выполняться на вашей машине в фоновом режиме до тех пор, пока не наступит время их запуска - тогда они поприветствуют вас на вашем тер- минале. Правда, это может причинить небольшое неудобство. Сообщение может появиться, когда вы работаете в редакторе, и нарушить содержимое экрана, но на самом деле оно не изменит ваш редактируемый файл. ПОЯСНЕНИЯ Вся программа представляет собой один большой оператор if -then-else в строках 4-9. Логика программы выглядит на псевдокоде сле- дующим образом: if it is morning если утро then echo morning statement то вывести "утреннее" приветствие else if it is noon иначе если день then echo noon statement то вывести "дневное" приветствие else echo evening statement иначе вывести "вечернее" приветствие В действительности программа гораздо сложнее, поэтому переведем дыхание и приступим к делу. В строке 4 проверяется текущее время на то, меньше ли оно 12 часов. Если да, то фраза команды expr выводит на стандартное уст- ройство вывода единицу ("1"). Поскольку символы ударения (`), которые обрамляют эту фразу, перехватывают стандартный вывод, символ 1 стано- вится частью оператора проверки, что указано квадратными скобками ([]). Затем оператор test проверяет, равен ли выход команды expr лите- ральной единице. Если они одинаковы, то в строке 5 выводится "утрен- нее" сообщение. Рассмотрим более подробно, как раскрывается оператор expr. Во-первых, он заключен в символы ударения. Это означает, что он выпол- няется перед оператором проверки. Затем его выход помещается для обра- ботки в оператор test. Однако внутри оператора expr имеется еще одно выражение между знаками ударения, которое выполняется до оператора expr. Такое старшинство выполнения управляется интерпретатором кода внутри shell. Внутренние знаки ударения сохраняются при начальном синтакси- ческом разборе строки, поскольку они экранированы символами обратной косой черты. Первой запускается команда date, имеющая в качестве выхо- да только текущее значение часа в соответствии с форматом %H. Затем expr использует данное значение часа для проверки, меньше ли оно 12. Если да, expr печатает единицу. Если значение часа больше или равно 12, то возвращаемое значение равно 0. Такое понимание, что 1=истина и 0=ложь, соответствует синтаксису, используемому в языке Си. Однако ранее мы замечали, что в среде программирования на языке shell 1 означает ложь, а 0 - истину. Это происходит потому, что прове- ряемое значение оператора if является в действительности статусом вы- хода из предварительно выполненной команды. Нуль соответствует нор- мальному завершению, поэтому 0 использован для переключения проверки в состояние "истина" и выполнения оператора then. Для того, чтобы преоб- разовать возвращаемый статус 1 (при условии, что значение часа меньше 12) в нуль (для переключения оператора then), мы используем команду test. Возвращаемый статус единицы равен константе 1, поэтому команда test возвращает 0, что представляет истину. Вот так! Если бы не были использованы вложенные знаки ударения, то единственным способом передачи данного типа информации другому про- цессу было бы применение переменных shell. Использование вложенной ко- мандной подстановки дает нам большую гибкость и простоту программиро- вания. Чем больше глубина вложенности, тем глубже экранирование знаков ударения. Порядок экранирования символами обратной косой черты такой: не нужно для внешней команды, один раз для второй внутренней команды, пять раз для третьей внутренней команды. На четвертом уровне их должно быть семь или девять (я еще не пробовал), но вероятно нет большой нуж- ды во вложенности такой глубины. Если проверка в строке 4 дает "ложь", выполняется строка 6. Это оператор else от первого if и одновременно следующий if. В таких осо- бых случаях синтаксис shell меняется. Ключевое слово "else" становится ключевым словом "elif". Второй if использует команду test точно так же, как и первый. Проверяемое время здесь 18, что представляет собой 6 часов вечера. Если вторая проверка также дает "ложь", выполняется последний оператор в строке 8. Этот else не использует команду test, поскольку после вы- полнения первых двух проверок мы можем сделать вывод, что остался последний период времени, а именно период после 18:00. -------------------------------------------------------- ИМЯ: lastlog -------------------------------------------------------- lastlog Сообщает время последней регистрации НАЗНАЧЕНИЕ Записывает и выводит на экран день и время вашей последней ре- гистрации в системе. ФОРМАТ ВЫЗОВА lastlog [-l] ПРИМЕР ВЫЗОВА lastlog Печатает дату, когда вы последний раз регистрировались ТЕКСТ ПРОГРАММЫ lastlog 1 : 2 # @(#) lastlog v1.0 Report last login time Author: Russ Sage 2а Сообщает время последней регистрации 3 4 if [ $# -gt 1 ] 5 then echo "lastlog: arg error" >&2 6 echo "usage: lastlog [-l]" >&2 7 exit 1 8 fi 9 10 if [ "$#" -eq "1" ] 11 then if [ "$1" = "-l" ] 12 then date >> $HOME/.lastlog 13 lastlog 14 else echo "lastlog: unrecognized option $1" >&2 15 echo "usage: lastlog [-l]" >&2 16 exit 1 17 fi 18 else echo "Time of last login : `tail -2 $HOME/.lastlog | 19 (read FIRST; echo $FIRST)`" 20 fi ПЕРЕМЕННЫЕ СРЕДЫ ВЫПОЛНЕНИЯ FIRST Хранит первую из двух введенных строк HOME Хранит имя вашего регистрационного каталога ОПИСАНИЕ ЗАЧЕМ НАМ НУЖЕН lastlog? Одним из преимуществ работы в системе UNIX является то, что в ней совершается автоматическая запись вашего начального времени при каждом сеансе работы - вашего времени регистрации. Эта информация может быть полезной по нескольким причинам. Вопервых, вы можете запомнить, когда вы действительно работали в системе последний раз и проверять, не ре- гистрировался ли кто-нибудь под вашим паролем во время вашего отсутствия. Как мы увидим в главе 9, имеется ряд возможностей для то- го, чтобы кто-нибудь мог "заимствовать" ваш пароль без спроса. По этой причине многие коммерческие системы сообщают вам, когда вы регистриро- вались последний раз (или когда, по их мнению, вы это делали). Другой возможной причиной мог бы быть подсчет общего времени в конце сеанса работы. Вы могли бы использовать это как учетную информа- цию для себя или вычислительного центра. Немного позже мы представим средство, которое помогает при таких подсчетах. Разрабатываемое нами инструментальное средство должно иметь воз- можность записывать новые значения времени и выводить на экран время нашей последней регистрации. Важно, что данная программа может быть вызвана так, что она не изменяет файл с данными, но постоянно выводит время последней регистрации. ЧТО ДЕЛАЕТ lastlog? Lastlog - это программа, которая записывает время вашей регистра- ции при каждом входе в систему. Затем это время хранится в файле дан- ных в вашем регистрационном каталоге под именем $HOME/.lastlog. Имя файла lastlog начинается с точки с той целью, чтобы сделать его неви- димым для команды ls. Укрытие "служебных" файлов от распечатки по умолчанию несколько предохраняет от любопытных глаз, а также убирает эти файлы с дороги, когда вы просматриваете что-то другое. При вызове без опций lastlog печатает для нас дату последней ре- гистрации, получая запись из файла .lastlog. Для выполнения новой записи в файл .lastlog необходимо вызвать lastlog с опцией -l. При этом новое значение времени запишется в файл .lastlog, а затем командный файл lastlog вызовет сам себя для вывода на экран нового значения - небольшая рекурсия. Для того, чтобы программа lastlog работала автоматически, вы должны выполнять ее из вашего файла .profile во время регистрации. При таком способе она запишет последнее время в файл .lastlog. В качестве примера посмотрите файл .profile в первой главе. ПОЯСНЕНИЯ В строках 4-8 выполняется проверка на наличие ошибок. Если вы вызвали lastlog с числом аргументов больше одного, то это приведет к ошибке. Выводится сообщение на стандартное устройство регистрации оши- бок, и lastlog завершается со статусом ошибки 1. Строки 10-20 представляют собой оператор if-then-else, который показывает, был ли это вызов для записи нового значения времени или для печати старых значений. Если в строке 10 число позиционных параметров равно одному, то мы знаем, что либо этот параметр должен быть опцией -l, либо это ошибка. Следующий оператор if в строке 11 проверяет, является ли первый пози- ционный параметр опцией -l. Если да, то в файл $HOME/.lastlog добавля- ется текущая дата и lastlog вызывается снова без аргументов для печати предыдущей даты регистрации. (Мы только что видели, как это делается.) Если это не был аргумент -l, то строки 14-16 выполняют обработку ошиб- ки. Если число позиционных параметров равно нулю, выполняется опера- тор else в строке 18. Отсутствие опций означает, что мы хотим найти время нашей последней регистрации на машине и распечатать его. Это ка- жется довольно простым, но кто сказал, что машины просты? Если вы помните последовательность работы, то мы сперва регистри- руем новое время, а затем хотим найти время нашей предыдущей регистра- ции. Для файла .lastlog это означает, что наше текущее время регистра- ции находится в самом конце файла, а наше предыдущее время регистрации находится в строке непосредственно перед ним. Это значит, что мы долж- ны получить вторую строку от конца файла. Да уж. Как видно из строки 18, она занимается получением последних двух строк. Команда tail красиво выполняет эту работу. Нам нужен такой способ, чтобы мы могли прочитать именно первую строку, а вторую отб- росить, что выполняется в строке 19. Мы передаем по конвейеру выход команды tail подчиненному shell (указанному круглыми скобками), кото- рый читает первую строку и затем отображает ее. А что же со второй строкой? Она никогда не берется и пропадает. Другим способом может быть передача выхода команды tail по конвейеру команде "head -1". Поскольку эта команда не имеет других опций, мы не даем никаких примеров. Тем не менее, давайте теперь рассмотрим наше другое средство регистрации времени входа в систему. -------------------------------------------------------- ИМЯ: timelog -------------------------------------------------------- timelog Учет и статистика времени НАЗНАЧЕНИЕ Интерфейсное меню для слежения и сопровождения файлов регистрации времени. ФОРМАТ ВЫЗОВА timelog ПРИМЕР ВЫЗОВА timelog Выводит на экран главное меню, из которого можно выбирать необходимое действие ТЕКСТ ПРОГРАММЫ timelog 1 : 2 # @(#) timelog v1.0 Time accounting and statistics Author: Russ Sage 2а Учет и статистика времени 3 4 PROJ="" 5 6 while : 7 do 8 set `date` 9 echo " 10 11 $1, $2 $3 $4 12 13 Time Logger 14 ----------- Project: $PROJ 15 s) Select a project file 16 c) Create a new project file 17 l) List current project files 18 v) View the project file 19 n) Turn billing on 20 f) Turn billing off 21 r) Report ststistics 22 23 enter response (s,c,l,v,n,f,r,): \c" 24 25 read RSP 26 27 case $RSP in 28 "") break;; 29 s) echo "\Enter project name ( for exit): \c" 30 read PROJ2 31 if [ "$PROJ2" = "" ] 32 then continue 33 fi 34 if [ ! -s $PROJ2.time ] 35 then echo "you must specify a valid project file" 36 continue 37 fi 38 PROJ="$PROJ2";; 39 c) echo "\nEnter the new project name ( to exit): \c" 40 read PROJ2 41 if [ "PROJ2" = "" ] 42 then continue 43 fi 44 if [ -f "$PROJ2.time" ] 45 then echo "\n ** $PROJ2 already exists **" 46 continue 47 fi 48 PROJ="$PROJ2" 49 echo "\nProject file created: $PROJ" 50 echo "Project file created: `date`\nOFF: begin" > $PROJ.time;; 51 l) echo "\nCurrent project files:\n" 52 ls -l *.time 2>/dev/null || echo "no project files" | 53 sed "s/\.time//";; 54 v) if [ "$PROJ" = "" ] 55 then echo "you must select a project file first" 56 continue 57 fi 58 echo "\n:----------------------------" 59 more $PROJ.time 60 echo ":---------------------------";; 61 n) if [ "$PROJ" = "" ] 62 then echo "you must select a project file first" 63 continue 64 fi 65 if [ "`tail -1 $PROJ.time|cut -d: -f1`" != "OFF" ] 66 then echo "logging was not turned off" 67 continue 68 fi 69 echo "\nBilling turned on for project file: $PROJ" 70 echo "ON: `date`" >> $PROJ.time;; 71 f) if [ "$PROJ" = "" ] 72 then echo "you must select a project file first" 73 continue 74 fi 75 if [ "`tail -1 $PROJ.time|cut -d: -f1`" != "ON" ] 76 then echo "logging was not turned on" 77 continue 78 fi 79 echo "\nBilling turned off for project file: $PROJ" 80 echo "OFF: `date`" >> $PROJ.time;; 81 r) while : 82 do 83 echo " 84 Statistics 85 ---------- Project: $PROJ 86 a) Accumulative time totals 87 n) All times on 88 f) All times off 89 90 enter response (a,n,f,): \c" 91 92 read RSP 93 94 case $RSP in 95 "") break;; 96 a) awk '/Total:/ { PRINT $0 }' $PROJ.TIME;; 97 n) awk '/ON/ { print $0 }' $PROJ.time;; 98 f) awk '/OFF/ { print $0 }' $PROJ.time;; 99 *) echo "\n ** Wrong command, try again **";; 100 esac 101 done;; 102 *) echo "\n ** Wrong command, try again **";; 103 esac 104 done ПРЕМЕННЫЕ СРЕДЫ ВЫПОЛНЕНИЯ PROJ Содержит текущее имя проекта PROJ2 Содержит временное имя проекта, введенное пользователем RSP Содержит команду выбора из меню ОПИСАНИЕ ЗАЧЕМ НАМ НУЖЕН timelog? Время - драгоценный товар. Его всегда не хватает, и если уж оно использовано, то его никогда больше нельзя вернуть. Мы хотим быть уве- рены, что наше время используется плодотворно. Мы можем и должны соз- дать инструментальные средства, которые помогут нам управлять нашим временем и фиксировать его. Мы должны рассмотреть следующие вопросы: над какими проектами мы работаем, в течение какого времени мы над ними работаем (т.е. начало и окончание), и какую еще информацию нам нужно хранить. Как раз почти все эти функции поддерживаются программой timelog. Мы написали довольно длинную программу, но заметим, что на самом деле работа над этим средством не окончена. Предлагаемый командный файл timelog подготавливает вас к работе с системой управления временем. Вам нужно вставить ваш собственный текст для выдачи отчетов, основан- ных на статистике времени. ЧТО ДЕЛАЕТ timelog? Timelog относится к весьма важной области, связанной с фиксацией времени и управлением временем. Отметим, что количество учетных сведе- ний, которые можно создать, просмотреть и обработать, ограничено толь- ко доступным пространством файловой памяти. Timelog - это полностью управляемый с помощью меню интерфейс. Системы с меню в UNIX - это нечто новое, они имеют свои преимущества и недостатки. Одним из преимуществ является то, что вся работа над дан- ными выполняется программно, а не вручную. Кроме того, каждую функцию наглядно видно и легко выбрать. Вам нет необходимости запоминать опции и имена файлов, достаточно просто нажать одну клавишу для выполнения действия. Недостатком является то, что меню работают медленнее, чем ручной интерфейс (т.е. просто набор и непосредственное выполнение команд). Это очень важное замечание, но мы должны также помнить, что программы должны быть простыми в использовании, простыми для модификации и вы- полнять множество мелочей, связанных с какой-либо идеей или областью назначения. Потеря машинного времени чаще всего лучше, чем потеря вре- мени человека! Другой недостаток - для того чтобы добраться до опреде- ленной функции, вы должны пройти через несколько уровней меню. Например, чтобы напечатать отчет, вы должны вызвать timelog, выб- рать меню статистики, затем выбрать нужный вам отчет. Здесь три уров- ня, а при наличии утилиты вы могли бы всего одной командой сказать "report report_file". Для утилит, выполняющих одну функцию, наличие одной команды с несколькими опциями довольно эффективно. Такой подход применяется в большинстве командных файлов интерпретатора shell. Но когда у вас есть множуство небольших задач, выполняемых над группой объектов, меню бо- лее удобны. Некоторые системы предоставляют интерфейс, управляемый как меню, так и командами. Это устраивает больший круг пользователей и позволяет избежать большинства недостатков, упомянутых выше. Конечно, при таком подходе неминуемы некоторые издержки и программа становится более длинной. При вызове timelog на экран выводится начальное меню, как показа- но ниже. --------------------- | | Thu, Jun 19 21:32:12 | | Time Logger | ----------- Project: | s) Select a project file | c) Create a new project file | l) List current project files | v) View the project file | n) Turn billing on | f) Turn billing off | r) Report statistics | | enter response (s,c,l,v,n,f,r,): В левом верхнем углу показан день недели и дата. В правом верхнем углу показано время. Это реальное время, и оно обновляется при каждом вызове меню. Имя меню "Time Logger" (регистратор времени). "Report statistics" (сообщить статистику) вызывает появление подчиненного ме- ню. Строка, в которой написано "Project:" (проект), показывает, что текущее имя проекта нулевое. Для того чтобы работать над проектом, вы сперва должны создать файл проекта или выбрать его, если он уже су- ществует. Все действия, выполняемые после этого, относятся к текущему файлу проекта. Первый пункт меню s предназначен для выбора файла проекта. После выбора этого пункта выводится сообщение: --------------------------- | | Enter project name ( for exit): | Введите имя проекта ( для выхода): Вы можете ввести любую текстовую строку в качестве имени проекта или, если вам не нужна эта опция, нажать клавишу возврата каретки для благополучного выхода. Если вы не помните имена проектов, вы можете использовать опцию l, поясняемую ниже. После ввода имени существующего проекта, текущему имени проекта (которое печатается справа вверху в каждом меню) присваивается имя этого файла. Следующей является опция c для создания файла проекта. Как уже отмечалось, это должно быть первым, что вы делаете, начиная работать с утилитой timelog, но после этого вы обычно выбираете существующие фай- лы. Когда вы выбрали опцию c, печатается следующее приглашение: --------------------------- | | Enter the new project name ( to exit): | Введите имя нового проекта ( для выхода): Здесь нужно вводить то же самое, что и при выборе проекта. Для выхода нажмите возврат каретки. После ввода имени текущее имя проекта изменяется, создается файл проекта, запоминается время, и файл загру- жается исходной информацией. Следующая опция l предназначена для выдачи списка имен файлов проектов. Поскольку каждый проект является файлом, отображается список в виде, обычном для команды ls. Тем не менее, будьте внимательны. Список нельзя получить прямо командой ls. Имена изменены для защиты от наивных. Каждый файл проекта хранится на диске в формате "project. time". Часть project в каждом файле отличается и представляет собой имя, вве- денное в опции создания. Все файлы имеют суффикс .time. Когда выво- дится список, префикс .time отбрасывается, так что имена файлов явля- ются просто проектами, которые вы ввели в опции выбора проекта. Здесь все работает, но вы должны помнить, что если вы захотите просмотреть файлы времен вручную, то имена не будут теми же самыми. Если нет ника- ких файлов проектов, то об этом выводится сообщение. Следующей опцией является v для просмотра файла проекта. Файлом, который вы собираетесь просмотреть, является текущий файл проекта. Его имя выводится в меню справа от слова "Project:". Если не появилось ни- какого имени, вы должны сперва создать новый проект или выбрать су- ществующий. Файл проекта выводится на экран командой UNIX more. Следующей опцией является опция n для включения подсчета времени. Это означает начало записи нового сеанса работы над проектом. Проверя- ется имя проекта, чтобы выяснить, был ли выбран файл проекта. Если нет, выводится сообщение о том, что нужно это сделать. Затем проверя- ется, был ли файл проекта отключен предыдущей операцией. Если да, то регистратор времени может быть включен. Вы не можете включить его дважды. Вы должны отключить его, затем включить и т.д. Следующая опция f отключает подсчет времени для файла проекта. Текущее имя проекта сравнивается с нулевым, и если это так, то выво- дится соответствующее сообщение. Затем проверяется, был ли предвари- тельно включен подсчет времени для этого файла. Если был, то в файл проекта добавляется запись о выключении подсчета. Последней опцией является r для отчета и статистики. После ее вы- бора на экран выводится подчиненное меню: ----------------------- | | Statistics | ---------- Project: | a) Accumulative time totals | n) All times on | f) All times off | | enter response (a,n,f,): Как упоминалось ранее, это меню на самом деле не реализовано. Несколько команд-заглушек позволяют этому меню функционировать, но в этом месте вы можете настроить отчеты по вашим требованиям. Обратите внимание, что имя проекта также выводится в этом меню. Это имя затем доступно для любых функций, помещенных в данное меню. ПРИМЕРЫ 1. c,l,v Это первый набор команд при первоначальном запуске. Опция c - пункт меню для создания файла проекта. Команда l выводит список всех имен файлов проектов, а v просматривает исходные данные, находящиеся в файле проекта. 2. n,n Такая последовательность иллюстрирует проверку на ошибки внутри программы. Сначала включается подсчет времени для текущего файла про- екта, а затем он включается опять. Timelog распознает это и сообщает, что вы должны отключить подсчет перед тем, как снова включить его. 3. s,junk Эта последовательность также иллюстрирует проверку ошибок. Пыта- емся выбрать новое имя файла проекта. Имя файла junk (которого вы не имеете). Timelog проверяет, существует ли файл регистрации времени с именем junk. Если нет, выводится сообщение о том, что вы должны выб- рать правильное имя файла проекта. ПОЯСНЕНИЯ В этом командном файле много текста, но замысел не очень сложен. Строка 4 инициализирует переменную PROJ нулевым значением. PROJ - это переменная, которая содержит имя проекта, отображаемое в меню. В начале работы мы должны быть уверены, что эта переменная установлена в нуль. Строки 6-104 - это огромный бесконечный цикл while, который вы- полняет работу всей программы. Поскольку это бесконечный цикл, мы мо- жем выйти из него либо оператором break, (вводя обычные символы преры- вания), либо с помощью команды выхода. В строке 8 позиционным параметрам присваивается результат команды date. Поступая таким образом, мы можем затем легко обращаться к каждо- му полю без выделения его командой cut - shell выполняет за нас син- таксический разбор полей. Мы можем ссылаться на поля даты в виде $1, $2 и т.д. Строки 9-23 выводят на экран главное меню. Верхняя строка обраща- ется к данным из команды date. $1, $2 и $3 представляют собой день не- дели, месяц и число. $4 - это время. Перед тем как команда echo выво- дит текстовые строки, эти переменные раскрываются таким образом, что они появляются в меню. Если переменная PROJ равна нулю, то ничего не печатается в качестве имени текущего проекта. Символы \c в конце ог- ромного оператора echo устанавливают курсор после приглашения в этой же строке, так что мы готовы принимать вводимые пользователем символы. После печати меню в переменную RSP читается ответ в строке 25. Затем наступает черед огромного оператора case (строки 27-103), кото- рый содержит ветку для каждой команды. В строке 28 проверяется, не был ли ответ всего лишь возвратом ка- ретки, указывающим, что пользователь хочет выйти. Если был, то цикл while завершается посредством команды break и программа заканчивает работу. Иногда возврат каретки - более желательный метод выхода, чем ко- манда exit интерпретатора shell. В конечном итоге эта команда shell приводит к выполнению программ exit и _exit Си -интерфейса. Выполнение вызова exit в Си иногда приводит к неожиданным побочным эффектам, в то время как нормальное выполнение текста программы до конца не дает та- ких же результатов. Однажды мы столкнулись с такой проблемой при использовании ESC -последовательностей для изменения цвета на цветном мониторе. Когда программа завершалась нормально, цвет не переустанав- ливался. Однако когда был сделан системный вызов exit, печатались не- которые ESC-последовательности, что переустанавливало отдельные части экрана. Очень странно! Строки 29-38 управляют функцией выбора проекта. Имя проекта зап- рашивается и читается в переменную PROJ2. PROJ2 использована для вре- менного хранения этого значения. Если был введен возврат каретки, опе- ратор continue приводит к следующей итерации внешнего цикла while. Это позволяет пользователю прекратить выполнение этой функции при ошибоч- ном вводе, оставаясь все же в timelog. Если ввод был непустым, файл проекта проверяется на существование и на наличие в нем данных. Если файл не существует, пользователя просят указать верное имя проекта. Если имя файла правильное, то переменной PROJ присваивается значение PROJ2. Только после того, как командный файл с уверенностью знает, что имя, введенное пользователем, допустимо, оно назначается в качестве текущего имени проекта. Это предохраняет от потери выбора текущего проекта из-за ошибки пользователя. Теперь PROJ выводится в меню на эк- ран. Команда создания обрабатывается строками 39-50. Снова запрашива- ется имя и проверяется, не равно ли оно нулю. Если имя было введено, то проверяется, существует ли уже такой файл. Мы не хотим снова созда- вать и затирать уже имеющийся файл. Файл создается в строке 50. В этот файл выводятся отметка о времени его инициализации и начальное сообще- ние о том, что подсчет времени отключен. Опция вывода списка выполняется в строках 51-53. Выводится заго- ловок, а затем команда ls используется для генерации списка. Если нет файлов нужного нам типа, то команда ls возвращает статус, отличный от нуля, тем самым включается оператор ||. В сущности этот фокус shell дает нам встроенный оператор if-then, который может использовать ре- зультаты предыдущей команды. Если выполнение команды ls неудачно (т.е. не найдены подходящие файлы), это сообщение об ошибке отбрасывается (не выводится) и выполняется оператор echo. Команда echo сообщает, что нет файлов, чтобы вы знали об этом. Всякий выход команды ls пропускается через команду sed для отб- расывания расширения имени файла .time. Для сохранения места и для удобства пользователя мы хотим только посмотреть и напечатать имена проектов, а не имена файлов. Однако, мы хотим хранить имена файлов в специальном внутреннем формате так, чтобы мы могли проще обрабатывать их и поддерживать уникальные имена. Команда просмотра выполняется в строках 54-60. Текущий файл про- екта проверяется на то, было ли выбрано имя. Если нет, главное меню выводится снова. В противном случае печатается строка из черточек, вы- водится командой more файл проекта и печатается еще одна строка черто- чек для обрамления выведенной информации. Вы можете удивиться, почему первым символом оператора echo является двоеточие (:). Это некоторый казус, поскольку, если вы попытаетесь отобразить символ черточки (-) в качестве первого символа, то оператор echo "подумает", что это пустая опция и не выведет ее на экран. Вы просто должны поставить в первой позиции какой-то непустой символ, отличный от черточки. Включение подсчета времени выполняется в строках 61-70. Текущее имя проекта проверяется на то, было ли оно выбрано. Если да, то прове- ряется, был ли отключен файл проекта. Мы выполняем это, используя ко- манду tail для выделения последней строки файла, затем передаем по конвейеру эту строку команде cut, где мы изменяем символ-разделитель на символ : и отрезаем первое поле. Если в этом поле находятся символы OFF, все в порядке. После этого строка 69 выводит на экран сообщение для пользователя, а строка 70 вставляет в файл проекта строку ON, за которой следует текущая дата. Тем самым файл отмечается как включен- ный. Подсчет времени начался. Если эта операция уже была включена, мы сообщаем об этом пользователю и выходим из данной операции меню. Строки 71-80 обрабатывают отключение подсчета времени. Здесь поч- ти все идентично тексту программы, который включает эту операцию, только если там было слово "on", то здесь слово "off". Строки 81-101 обрабатывают подчиненное меню выдачи отчетов и ста- тистики. Как видите, экранное меню спланировано таким же образом: цикл while, печать меню, чтение ответа, выполнение оператора case в зависи- мости от выбранной команды и т.д. Команда r подобна главному меню, только сокращена для того, чтобы поместиться внутри оператора case. Вы также можете заметить, что в строках 96-99 выполняется не очень много обработки. Это то место, где вы должны выполнить некоторую работу. Строка 102 выполняет обработку ошибок при любом неверном вводе. Печатается сообщение об ошибке, оператор case выходит на следующую итерацию цикла while, и все начинается сначала. МОДИФИКАЦИИ Основной возможностью для модификации является добавление факти- ческой обработки информации о времени. Один из подходов к этому - зап- росить почасовой тариф времени в момент создания файла проекта (если считать, что вы работаете на таких основаниях). Этот тариф может хра- ниться первым пунктом в файле проекта. Следующие две строки могут быть отведены для "счета по текущему сеансу" и "общего счета" соответствен- но. Когда подсчет времени работы над проектом отключается, можно проа- нализировать текущее системное время и начальное время подсчета и за- тем пересчитать в минуты (для упрощения арифметики) с использованием команды expr (или, возможно, awk). Затем это значение можно умножить на хранимый в файле тариф времени, а результат сохранить в записи о текущем сеансе работы и ДОБАВИТЬ к совокупной общей записи. ----------------------------------------------------- ИМЯ: today ------------------------------------------------------ today Печать календаря с подсвеченной сегодняшней датой НАЗНАЧЕНИЕ Модифицирует вывод утилиты cal для печати сегодняшней даты ин- версным цветом. ФОРМАТ ВЫЗОВА today ПРИМЕР ВЫЗОВА today Печатает календарь на этот месяц с подсвеченной сегодняшней датой ТЕКСТ ПРОГРАММЫ today 1 : 2 # @(#) today v1.0 Calendar with today highlighted Author: Russ Sage 2а Календарь с подсветкой сегодняшнего дня 3 4 SYSV=n 5 6 set `date` 7 8 if [ "$SYSV" = "y" ] 9 then RVR=`tput smso` 10 BLNK=`tput blink` 11 NORM=`tput rmso` 12 cal ${2} ${6} | sed "s/${3}/${RVR}${BLNK}${3}${NORM} /g" -e "s/^/ /" 13 else RVR="^[[7m" # termcap so 14 NORM="^[[0m" # termcap se 15 cal ${2} ${6} | sed -e "s/ ${3}/ ${RVR}${3}${NORM} /" -e "s/^/ /" 16 fi ПЕРЕМЕННЫЕ СРЕДЫ ВЫПОЛНЕНИЯ RVR Управляющий символ инверсного отображения для вашего терминала BLNK Управляющий символ мерцания для вашего терминала, если таковой имеется NORM Управляющий символ для возврата терминала в обычный режим ОПИСАНИЕ ЗАЧЕМ НАМ НУЖЕН today? Всегда приятно иметь инструментальные средства, предоставляющие нам информацию о нашей среде. Тип информации, который мы здесь рассматриваем - это модифицированный вывод календаря. Стандартная ко- манда cal выводит дни месяца, но не сообщает вам, какой день сегодня. Как мы это узнаем? Мы должны запустить команду date для определения текущего дня. Довольно тривиальная задача, однако наше решение может дать некоторую ценную графику, которая может добавить действительно полезную информацию к календарю. Для выполнения такой модификации нам необходимо выполнить постоб- работку результата команды cal. Поскольку не все терминалы обладают одинаковыми возможностями, эта программа должна быть приспособлена к вашей машине. ЧТО ДЕЛАЕТ today? Today - это постпроцессор для команды cal, который делает ее ре- зультат более информативным и графически наглядным. Выполнение модифи- кации команды cal зависит от того, в какой системе вы работаете. Если вы в системе UNIX System V (версия 2 или старше), то у вас есть утили- та terminfo. Terminfo является заменой для файла termcap и поставля- ется с несколькими утилитами, которые возвращают значения с информаци- ей о ПРИЕМЫ ПРОФЕССИОНАЛЬНОЙ РАБОТЫ В UNIX нальных характеристик. Если ваш компьютер работает не с системой System V, то вам необходимо немного поисследовать тип ваших конкретных терминалов и внести полученные значения в вашу программу. Вся история с утилитами termcap и terminfo иллюстрирует эволюцию работы в среде UNIX. UNIX с самых первых дней стремился быть независи- мым от типов устройств. Первым шагом явилось использование файлов уст- ройств и драйверов. Следующим шагом был файл termcap, который пре- доставил единообразный способ получения информации о терминалах. Последним этапом является утилита terminfo, предоставляющая эту инфор- мацию таким способом, который лучше обеспечивает функциональный доступ из программ. Поскольку в пределах командного файла не так легко определить, с какой системой работает ваш компьютер, использована переменная SYSV. Эту переменную можно изменить при помощи редактора, поэтому today мо- жет работать в разных системах. Способами выяснения системы могли бы служить программа uname, существование определенных shell-программ в каталоге /bin или какой-нибудь системный файл, содержащий номер версии. По умолчанию переменная SYSV установлена так, чтобы утилита today работала НЕ в системе System V. С этим связано существование фрагмента программы, который нужно изменять вручную. Как вы можете самостоятельно получить информацию о терминале? Каждый терминал имеет свои специфические особенности. Все терминалы характеризуются в основном файлом описания терминала termcap. В этом файле каждая характеристика дается под своим именем вместе с аппарат- ным кодом этой функции. Таким образом, мы можем, например, редактором vi заглянуть в termcap и увидеть, как управлять терминалом, на котором мы работаем. Файл termcap сильно зашифрован и загадочен. По данному вопросу не очень много документации, что порождает множество экспери- ментов и ошибок. Переменными, которые нас интересуют, являются "so" для выделения информации (инверсный режим) и "se" для завершения выделения (обычный режим), а также режим мерцания, если он есть у вашего терминала. Termcap, похоже, не содержит информацию о том, как включить режим мер- цания, поэтому вам, вероятно, нужно будет для этого посмотреть доку- ментацию на терминал. В системе System V (версия 2) команда tput возв- ращает соответствующее значение. По умолчанию в today выполнены установки для ANSI терминала, яв- ляющегося консольным в системе XENIX. Эти коды были вручную извлечены из файла /etc/termcap и вставлены в текст программы. Если ваши коды отличаются, вы должны выяснить их. Обратите внимание, что в файле /etc/termcap символ ESCAPE представлен как \E. Это не годится для today, и вы должны изменить такое представление на настоящий ESCAPE. Поскольку ESCAPE является символом выхода из режима ввода в редакторе vi, вы должны использовать команду control-V в этом редакторе для вво- да управляющих символов. Последовательность control-V вызывает печать символа ^, а ESCAPE - печать символа [. Таким образом, реальная коман- да входа в инверсный режим в редакторе vi представлена последователь- ностью ^[[7m. Эта команда включает символы ^[ в качестве ESCAPE и за- тем обычные символы [7m для изменения режима. Теперь, когда характеристики терминала учтены, цель утилиты today - выделить текущий день календаря в инверсном виде, а все остальное оставить в обычном виде. Это делается путем передачи по конвейеру вы- хода команды cal команде sed. Утилита sed находит число в выходных данных и подставляет специальную графическую ESC-последовательность. Поскольку ваш терминал использует специальные символы для изменения режима, вы не увидите их выдачи на экран. Данная программа не имеет опций или какого-то особого входа. Она распечатывает календарь с отмеченным сегодняшним днем. ПОЯСНЕНИЯ В строке 4 выполняется инициализация переменной SYSV значением "n". Это заставляет программу переходить к особой области, в которой жестко закодированы управляющие коды терминала, определенные вручную. Если вы работаете с последними версиями системы System V, то вам нуж- но, чтобы эта переменная имела значение "y". В строке 6 позиционным параметрам присваивается результат команды date. Мы обратимся к этим значениям позже. Строки 8-16 - это остальная часть программы. Они представляют со- бой один оператор if-then-else. Строки 9-12 поддерживают принятый в System V метод tput для получения характеристик терминала, а строки 13-15 управляют ручным способом их получения. В обоих случаях переменным shell присваиваются ESC-последователь- ности. Эти значения используются позже. В обоих случаях выполняется вызов команды cal с использованием значений месяца и года, полученных от команды date. Этот образ календаря пропускается по конвейеру через утилиту sed, которая ищет указанный день "сегодня", также полученный от команды date. Когда этот день найден, sed заменяет цифры дня на последовательность включения инверсного режима, последовательность включения режима мерцания, если он есть, и символы дня, а затем после- довательность возврата в обычный режим работы терминала. Последняя ко- манда sed заполняет пробелами начало строки для размещения ее в центре экрана. УПРАВЛЕНИЕ ДЕЛОПРОИЗВОДСТВОМ Много рабочего времени тратится на запоминание важной информации, такой как деловые встречи, адреса, номера телефонов, расписания, учет- ные сведения о проектах и так далее. Большинство из такой информации может быть сохранено в системе UNIX в виде простых структурированных текстовых файлов, которыми можно манипулировать с помощью соответству- ющих средств. Автоматизация этой области может освободить много време- ни для выполнения "настоящей" работы. Хотя мощные коммерческие программы для сохранения такой информа- ции имеются в среде MS-DOS, эти программы не имеют широкого распрост- ранения в UNIX. Часто у вас нет необходимости в отдельной большой программе для выполнения таких работ в UNIX. UNIX обеспечивает хороший компромисс между легкими в применении, но не гибкими коммерческими программами и программированием на мощных, но непростых в использова- нии традиционных языках программирования. Возможности языка shell плюс богатый ассортимент встроенных команд UNIX дают мощный, гибкий и СРАВ- НИТЕЛЬНО простой в использовании компромисс. В данном разделе мы представляем широкий набор средств управления личной информацией, ко- торые вы можете приспособить для своих нужд. Для шутки начнем с программы по имени jargon - генератора техни- ческих терминов, конструирующего замысловатые фразы. С его помощью можно удивлять людей тем, как много вы знаете, или начать прибыльную вторую карьеру в качестве составителя рекламы. Комбинирование слов по- рождает сотни фраз. Затем мы рассмотрим программу phone. Phone - это командный файл, управляемый с помощью меню, который поддерживает базу телефонных номе- ров и сопутствующей информации. Она сводит воедино разнообразные аспекты сопровождения базы данных и обслуживания запросов к ней. Последнее инструментальное средство называется office. Это управ- ляемая при помощи меню утилита, которая дает одноклавишный доступ ко всем функциям делопроизводства. Сюда относятся почта, новости, кален- дарь, номера телефонов и автоматические напоминания. ------------------------------------------------------- ИМЯ: jargon ------------------------------------------------------- НАЗНАЧЕНИЕ Компьютеризованная версия старого настольного генератора техни- ческих терминов. ФОРМАТ ВЫЗОВА jargon ПРИМЕР ВЫЗОВА jargon Если ввести 125 в ответ на запрос, то на выходе 125 получится фраза Total Monitored Concept ТЕКСТ ПРОГРАММЫ jargon 1 : 2 # @(#) jargon v1.0 Technical jargon generator Author: Russ Sage 2а Генератор технического жаргона 3 4 echo "\n\t\tThe Jargon Generator" 5 while : 6 do 7 echo "\nEnter a 3 digit number (000-999), ? , or : \c" 8 read NUM 9 10 case $NUM in 11 "") exit;; 12 \?) cat < /dev/ null || 27 { echo "\nNot a valid number, try again "; continue; };; 28 *) echo "\nInvalid input, try again" 29 continue;; 30 esac 31 32 N1=`echo $NUM|cut -c1` 33 N2=`echo $NUM|cut -c2` 34 N3=`echo $NUM|cut -c3` 35 SEN="" 36 37 case $N1 in 38 0) SEN="${SEN}Integrated ";; 39 1) SEN="${SEN}Total ";; 40 2) SEN="${SEN}Systematized ";; 41 3) SEN="${SEN}Parallel ";; 42 4) SEN="${SEN}Functional ";; 43 5) SEN="${SEN}Responsive ";; 44 6) SEN="${SEN}Optional ";; 45 7) SEN="${SEN}Synchronized ";; 46 8) SEN="${SEN}Compatible ";; 47 9) SEN="${SEN}Balanced ";; 48 esac 49 50 case $N2 in 51 0) SEN="${SEN}Management ";; 52 1) SEN="${SEN}Organizational ";; 53 2) SEN="${SEN}Monitored ";; 54 3) SEN="${SEN}Reciprocal ";; 55 4) SEN="${SEN}Digital ";; 56 5) SEN="${SEN}Logistical ";; 57 6) SEN="${SEN}Transitional ";; 58 7) SEN="${SEN}Incremental ";; 59 8) SEN="${SEN}Operational ";; 60 9) SEN="${SEN}Third-Generation ";; 61 esac 62 63 case $N3 in 64 0) SEN="${SEN}Options";; 65 1) SEN="${SEN}Flexibility";; 66 2) SEN="${SEN}Capability";; 67 3) SEN="${SEN}Mobility";; 68 4) SEN="${SEN}Programming";; 69 5) SEN="${SEN}Concept";; 70 6) SEN="${SEN}Time-Phase";; 71 7) SEN="${SEN}Projection";; 72 8) SEN="${SEN}Hardware";; 73 9) SEN="${SEN}Contingency";; 74 esac 75 76 echo "\n\"$SEN\"" 77 done ПЕРЕМЕННЫЕ СРЕДЫ ВЫПОЛНЕНИЯ N1 Первая цифра числа N2 Вторая цифра числа N3 Третья цифра числа NUM Число, введенное пользователем с клавиатуры SEN Предложение, полученное из найденных слов ОПИСАНИЕ ЗАЧЕМ НАМ НУЖЕН jargon? В нашем скоростном, заполненном техникой мире на нас оказывается большое давление с целью получения каких-то результатов. К сожалению, быстрота современной техники относится к выполнению программ, а не к их созданию. Когда мы сделали всего лишь третью часть программы, а уже пора ее предъявлять на рынок - как мы можем создать впечатление, что наш продукт делает больше, чем на самом деле? Необходимо немного пустить пыль в глаза заказчику. Пускание пыли состоит из фраз, которые выглядят и звучат впечатляюще. При строгой проверке мы можем увидеть, что фраза составлена из настоящих английских слов, значение которых можно посмотреть в словаре. Эта фраза в целом совершенно бессмысленна, но если нам повезет, то читатель этого не заметит! Одним из простых способов получения таких вещей является наличие таблицы взаимозаменяемых слов, которые можно использовать для состав- ления предложений. Если у вас система BSD, то вы можете запустить jargon вместе с программой fortune, которая имитирует случайные подар- ки судьбы. Мудрость веков может принадлежать вам! ЧТО ДЕЛАЕТ jargon? Jargon - это инструмент для генерации фраз, состоящих из техни- ческих терминов. Строится фраза, которая является комбинацией трех слов. Первые два слова являются прилагательными, используемыми для описания третьего слова, имени существительного. Каждое слово выбира- ется из столбца, содержащего десять возможных слов. Именно это придает программе jargon ее творческие способности. Она может объединять слово из одного столбца с любым словом из другого столбца для построения многих полезных фраз. Если вы хотите увидеть всю таблицу слов, введите символ ?. Вот пример таблицы: ----------------------------- | | The Jargon Generator | | Enter a 3 digit number (000-999), ?, or : ? | | 0. Integrated 0. Management 0. Options | 1. Total 1. Organizational 1. Flexibility | 2. Systematized 2. Monitored 2. Capability | 3. Parallel 3. Reciprocal 3. Mobility | 4. Functional 4. Digital 4. Programming | 5. Responsive 5. Logistical 5. Concept | 6. Optional 6. Transitional 6. Time-Phase | 7. Synchronized 7. Incremental 7. Projection | 8. Compatible 8. Operational 8. Hardware | 9. Balanced 9. Third-Generation 9. Contingency | Имеется три возможных варианта ввода. Вы можете ввести трехзнач- ное число, знак вопроса для печати таблицы или возврат каретки для вы- хода из программы. Проверяется, является ли строка из трех цифр до- пустимым числом. Если все в порядке, то введенное число разбивается на три составляющие его цифры. Каждая цифра используется как ключ поиска в массиве из десяти слов для получения куска предложения. Затем все слова объединяются для формирования жаргонной фразы. После этого вам выдается запрос на дальнейший ввод. Если вы ввели знак вопроса, печатается таблица слов, как показано в предыдущем примере, и вам снова выдается запрос. Если был введен только возврат каретки, то цикл, выдававший запросы, завершается и за- канчивается выполнение программы. Внутри самой программы выполняется только проверка на ошибки. Программа jargon организована аналогично программе, управляемой с по- мощью меню, однако никакого меню нет. Это просто цикл, который выпол- няется до тех пор, пока не будет нажат возврат каретки. ПРИМЕРЫ 1. $ jargon 898 Открывается секрет, что у меня персональный компьютер 898, то есть Compatible Third-Generation Hardware (совместимая аппаратура третьего поколения). 2. $ jargon 187 Оказывается это Total Operational Projection (всеобъемлющий раз- рабатываемый проект). ПОЯСНЕНИЯ Строка 4 печатает заголовок при начальном запуске программы. Отображается возврат каретки, две табуляции и сообщение. Строки 5-77 представляют собой один большой бесконечный цикл while. В нем имеется всего одна точка выхода, находящаяся внутри опе- ратора case. Строка 7 выводит приглашение, а строка 8 читает вводимый ответ в переменную NUM. Строки 10-30 являются оператором case, который проверяет информа- цию, введенную с клавиатуры. Если был введен только возврат каретки, строка 11 рассматривает это как нулевой ввод. В этом случае выполня- ется выход из программы. Это и есть нормальная точка выхода. Ввод вопросительного знака соответствует строке 12. Обратите вни- мание, что знак вопроса экранирован. Это выполнено по той причине, что символ ? имеет для shell специальное значение. Он используется как представитель любого одиночного символа при порождении имени файла. Для того, чтобы сделать знак вопроса обычным символом, мы должны экра- нировать его для отмены специального значения. В строках 12-24 команда cat получает текст из последующего фраг- мента самой программы. Такого рода файл иногда называют "встроенным документом". Возможность обработки встроенного документа активируется последовательностью символов <<. Слово, которое следует за ней, явля- ется признаком начала-окончания, в данном случае EOF. После того, как текст будет выведен на экран, строка 25 продолжает выполнение следую- щей итерации внешнего цикла while. Попутно отметим: для того, чтобы увидеть, как shell управляет встроенными документамм, посмотрите во время работы командного файла каталог /tmp. В нем находится файл с именем "shXXXX", где XXXX - иден- тификатор shell, создавшего этот файл. Весь встроенный документ цели- ком помещается в этот временный файл. Затем shell выполняет переад- ресацию своего входа на этот временный файл. Довольно простой метод. Строка 26 соответствует всем случаям ввода, когда имеется три символа. Эти символы могут быть буквами и/или цифрами. В этом месте shell еще не знает, есть ли там буквы. Для проверки того, что все вве- денные символы являются цифрами, мы должны использовать команду expr, выполняющую дополнительный анализ. Оператор expr указывает, что нужно сравнить строку NUM с последовательностью "начало строки, цифра, циф- ра, цифра, конец строки". Если сопоставление успешно, expr возвращает статус успешного возврата и программа идет дальше. Поскольку expr возвращает число совпавших символов, этот результат должен быть пере- направлен в каталог /dev/null. Если сравнение завершилось неудачей, активизируется оператор || (мы уже видели такого рода управляющую структуру ранее), который печа- тает сообщение об ошибке и вызывает следующую итерацию цикла while. Такой синтаксис представляет собой то же самое, что и оператор if-then-else. Поскольку за символами || может следовать список команд, то внутрь простых разделителей списка {} можно вставить более одной команды. Будьте внимательны. Если отсутствуют символы-разделители, то оператор continue выполнится как после сообщения об ошибке, ТАК И при успешном выполнении команды expr. Это может заставить вас заниматься отладкой, пока вы не обнаружите, что же произошло на самом деле. Точно такую же проверку числа можно было бы выполнить с помощью оператора case. Синтаксис был бы таким же, за исключением зацепочных символов ^ и $. Шаблон для оператора case выглядел бы так: [0-9][0-9][0-9] statement;; Я использовал оператор expr для того, чтобы показать, каким обра- зом expr может быть использован для выполнения такого рода проверки. Любой другой ввод перехватывается в строке 28 путем проверки на совпадение с универсальным символом-заменителем *. Выводится сообщение об ошибке, и оператор continue вызывает следующую итерацию цикла while, который запрашивает новый ввод. Обратите внимание, как shell рассматривает строки. Команда test фактически выполняет сравнение значения строки. Несмотря на то, что команде test(1) посвящена своя страница справочного руководства, она является встроенной функцией shell. Если при вызове команды test использован синтаксис =, !=, то два аргумента рассматриваются как строки. Но если в команде test используется синтаксис вида -lt, -eq, то производится сравнение двух аргументов-строк как чисел и выполня- ется их числовая обработка. Эти два различных режима нельзя смешивать, т.е. нельзя сравнивать строки при помощи числового оператора, например str1 -eq str2. В строках 32-34 каждая цифра вырезается из числа и помещается в свою собственную переменную. Затем эти переменные используются в ка- честве индекса в операторе case, который содержит магические слова. Строка 35 инициализирует переменную SEN для сбора предложения. (Пред- варительное замечание перед тем, как мы начнем получать письма от рев- нителей чистоты грамматики - да, мы знаем, что то, что мы генерируем, является фразой, а не настоящим предложением, поскольку отсутствует глагол.) Начинаем мы с пустого предложения и добавляем к нему каждый раз по одному слову. Строки 37-48 представляют собой первый оператор case. Оператор case берет значение переменной N1 и добавляет слово с таким номером к предложению. На самом деле нет необходимости включать значение пере- менной SEN в правую часть присваивания, поскольку еще ничего нет. Од- нако это делает текст программы более гибким, если мы решим предвари- тельно сгенерировать первоначальное предложение некоторыми другими средствами. Аналогичные операторы case обрабатывают две следующие циф- ры. Полученное предложение выводится в строке 76 после того, как най- дены все слова. Вы могли бы сказать, что вся эта штука представляет собой 754 подвиг, или Synchronized Logistical Programming (Синхронизи- рованное логическое программирование). МОДИФИКАЦИИ Можно немного поиграться с этой программой. Вы могли бы получать случайное число, зависящее от текущего системного времени (используя извлечение и команду expr) и основывать поиск на этом числе, а не на числе, введенном пользователем. Вы также могли бы использовать каждую цифру случайного числа для управления выбором слова из различных баз с жаргонными терминами, имея, возможно, по одному файлу для каждой из трех позиций слова и организуя цикл на случайное значение для чтения каждого слова. Слова могли бы читаться и удаляться из файла до тех пор, пока не прочитается слово, которое соответствует случайной цифре. Имеется множество других возможностей. Если вы вовремя не останови- тесь, то обнаружите, что вы написали компьютерную игру! -------------------------------------------------------- ИМЯ: phone -------------------------------------------------------- phone База данных с телефонными номерами НАЗНАЧЕНИЕ Управляемое меню средство, поддерживающее базу данных с телефон- ными номерами ФОРМАТ ВЫЗОВА phone ПРИМЕР ВЫЗОВА phone Вызов телефонной базы данных s Ввод опции поиска russ Поиск номера телефона Расса ТЕКСТ ПРОГРАММЫ phone 1 : 2 # @(#) phone v1.0 Maintain telephone database Author: Russ Sage 2а Сопровождение телефонной базы данных 3 4 if [ $# -gt 0 ] 5 then echo "phone: argument error" >&2 6 echo "usage: phone" >&2 7 exit 1 8 fi 9 10 BASE="$HOME/.phone.list" 11 12 while : 13 do 14 echo " 15 16 phonebase = $BASE 17 18 PHONE MENU 19 ---------- 20 add name to list 21 delete name from list 22 edit list 23 search for name in list 24 view complete list 25 - exit program 26 27 Press a,d,e,s,v or : \c" 28 read RSP 29 30 case $RSP in 31 "") exit 0 32 ;; 33 a|A) echo "\nEnter name to add ( to exit ): \c" 34 read NAME 35 if [ "$NAME" = "" ] 36 then continue 37 fi 38 echo "Enter description of person: \c" 39 read DESC 40 echo "Enter number to add: \c" 41 read NUM 42 echo "$NAME\t$DESC\t\t\t$NUM" >> $BASE 43 sort -t" " +1 -1.3b -o $BASE $BASE 44 ;; 45 d|D) echo "\nEnter name to delete ( to exit ): \c" 46 read NAME 47 if [ "$NAME" = "" ] 48 then continue 49 fi 50 sed -e "/$NAME/d" $BASE.new 51 mv $BASE.new $BASE 52 ;; 53 e|E) vi $BASE 54 ;; 55 s|S) echo "\nEnter name to search: \c" 56 read NAME 57 echo "\n----------------------------------" 58 grep -y "$NAME" $BASE 59 echo "------------------------------------" 60 ;; 61 v|V) echo "\n\tPhone List\n\t---------" & 62 more $BASE 63 echo "\nhit \c" 64 read RSP 65 ;; 66 *) echo "Not a valid command" 67 ;; 68 esac 69 done ПЕРЕМЕННЫЕ СРЕДЫ ВЫПОЛНЕНИЯ BASE Фактическое имя файла телефонной базы данных DESC Описание, вводимое в базу данных NAME Имя, вводимое в базу данных NUM Номер телефона, вводимый в базу данных RSP Ответ пользователя на приглашение ОПИСАНИЕ ЗАЧЕМ НАМ НУЖЕН phone? Телефоны представляют собой очень важную часть нашего рабочего дня. Мы используем их для сообщения своих планов, отдачи распоряжений и для многих других целей. Немного поразмышляем. Если почти на каждом рабочем месте в США установлен телефон, то телефонных номеров миллио- ны. Каким образом вы храните все ваши телефонные номера? Нам необходима очередная база данных. Эта база данных должна уп- равлять вводом данных, их извлечением, модификацией и удалением. У нас теперь есть опыт в реализации программ, управляемых с помощью меню, поэтому имеет смысл использовать меню и здесь. ЧТО ДЕЛАЕТ phone? Phone - это универсальная утилита для использования и управления базой данных телефонных номеров. Полностью управляемая при помощи ме- ню, phone содержит все необходимые функции, связанные с управлением базой данных: добавление, удаление, просмотр, редактирование и поиск. Файл, представляющий собой базу данных, размещен в вашем регист- рационном каталоге. Это файл $HOME/.phone.list. Phone использует ваш регистрационный каталог, так что один и тот же исполняемый модуль ра- ботает для любого пользователя. Независимо от того, где размещается командный файл phone или кто и откуда его запустил в вашей системе, вы всегда получите именно ваш файл телефонных номеров, поскольку он подк- лючен к вашему регистрационному каталогу посредством встроенной пере- менной shell с именем HOME. Имеется всего одно главное меню. Оно выводится на экран, и внизу появляется приглашение. Большинство вариантов выбора запрашивают до- полнительную информацию. Меню выглядит так: ------------------------ | | | phonebase = /usr/russ/.phone.list | | PHONE MENU | ---------- | add name to list (добавить имя к списку) | delete name from list (удалить имя из списка) | edit list (редактировать список) | search for name in list (поиск имени в списке) | view complete list (просмотр списка) | - exit program (выход из программы) | | Press a,d,e,s,v or : | В верхней строке меню выводится имя телефонной базы данных, кото- рую вы используете. Если это не ваш файл, вы точно знаете, что прои- зошла ошибка. Опции выбираются нажатием клавиши, соответствующей пер- вой букве слова. Если был нажат только возврат каретки, программа за- вершается. ПРИМЕРЫ 1. $ phone a russ sage unix master 123-4567 Такая последовательность действий используется для добавления еще одного номера в базу данных. Вызывайте меню, выбирайте опцию добавле- ния и в ответ на запросы утилиты phone вводите имя, описание и номер телефона. 2. $ phone V Просмотр всех записей в вашей базе данных. ПОЯСНЕНИЯ Строки 4-8 выполняют некоторую проверку на наличие ошибок. Если вы вызвали phone с какими-либо аргументами, выводится сообщение об ошибке и программа завершается. Поскольку phone управляется при помощи меню, она не использует аргументы. Строка 10 инициализирует переменную BASE именем файла телефонной базы данных. Это назначение выполняется с помощью переменной HOME для получения вашего регистрационного каталога. Строки 12-69 представляют собой один большой бесконечный цикл while. Вся эта структура подобна структуре других программ, управляе- мых с помощью меню, которые мы рассмотрели. Само меню выводится опера- тором echo. Текст меню НЕ выделен отступами подобно остальной части программы, поскольку оператор echo воспринимает этот текст как лите- ральные данные, включая пробелы. Строка 28 читает ответ пользователя, а строки 30-68 представляют собой оператор case, который проверяет значение ответа. Если был нуле- вой ввод, программа завершается. Если была введена буква a, выбирается опция добавления. (В действительности каждая опция проверяется и на верхнем, и на нижнем регистре для обеспечения гибкости). Текст программы, соответствующий добавлению, запрашивает имя, описание и номер телефона. Если вы оказа- лись в опции добавления случайно, используйте возможность возврата пу- тем нажатия возврата каретки в ответ на запрос имени. Однако после то- го, как вы ввели имя, выйти уже нельзя. Вам необходимо в этом случае поместить в базу фиктивную запись или нажать клавишу аварийного завер- шения для выхода из программы вообще. Описание может быть произвольной длины, однако оно не смотрится, если очень длинное. После того как все поля введены, вся строка помещается в конец базы данных. Отметим, что добавляются символы табуляции для разбиения записи на поля. После это- го база данных пересортировывается, чтобы новая запись заняла свое место в алфавитном порядке. Если была введена буква d, то она распознается в строке 45. Зап- рашивается удаляемое имя. Если вводится только возврат каретки, опера- тор continue возвращает нас обратно в цикл while и снова выводится главное меню. Для удаления записи использована команда sed, поэтому вводимое имя должно указываться точно в таком же виде, как оно хра- нится в файле. Результат удаления помещается во временный файл, кото- рый затем в строке 51 переименовывается в исходный файл базы данных. Одной из модификаций может быть показ пользователю того, что он соби- рается удалять, и запрос подтверждения. Если подтверждение получено, то данные удаляются окончательно. Сейчас сделано так, что вы на самом деле не знаете, что именно собираетесь удалять. Поэтому необходимо точно указывать удаляемое имя. Здесь выбран относительно быстрый и не совсем честный подход. А вообще вы даже могли бы использовать не- посредственно редактор vi для поиска и удаления. Опция редактирования в строке 53 выполняет только одно - вызывает редактор vi для работы с базой телефонных номеров. Используя редактор vi, вы можете вручную сделать файл базы данных таким, как вам нужно. Зачастую формат входных данных "плывет" из-за разной длины. Все данные хранятся в свободном формате и поэтому их можно как угодно смещать вдоль строки. Когда вы редактируете ваш файл редактором vi, все его команды ак- тивны. Действует даже выход в shell и выполнение обычных действий с системой. Поэтому не забудьте прекратить работу этого shell'а, иначе вы будете весьма удивлены в конце рабочего дня при выходе из системы. Для выхода из редактора и возврата к утилите phone используйте обычные методы, такие как ZZ, :x и :wq. Опция поиска в строке 55 запрашивает имя, которое нужно искать, читает это имя, выводит декоративные элементы до и после данных и использует утилиту grep для нахождения требуемой записи. Утилита grep в строке 58 использует команду -y, вызывающую нечувствительность к ре- гистру. Это означает, что вы можете производить ввод искомого элемента как на верхнем, так и на нижнем регистре. Если не найдено ничего, то ничего и не выводится на экран. Опция просмотра начинается со строки 61. Сначала выводится заго- ловок в фоновом режиме. Это делается для быстроты. На самом деле это просто эксперимент для того, чтобы увидеть, будут ли асинхронные собы- тия производить вывод на экран синхронно. В данном случае это именно так. Файл выводится командой more. Пока утилита more постранично выво- дит файл, все ее команды нам доступны. (Наш командный файл m в главе 4 иллюстрирует, что мы можем делать с помощью more.) В строке 63 выводится сообщение пользователю о том, что он должен нажать возврат каретки. Строка 64 выполняет чтение. Эта последователь- ность придерживает распечатанный файл данных на экране, чтобы его строки не пропали при сдвиге вверх. Если бы этих операторов не было, напечаталось бы главное меню и мы потеряли бы то, что находилось в верхней части экрана. Строка 66 выполняет проверку на ошибки для меню в целом. Если вы ввели данные, которые не соответствуют ни одному из предыдущих шабло- нов, то им сопоставляется шаблон *. В этом случае печатается сообщение об ошибке, управление переходит из оператора case в конец цикла while и при следующей итерации этого цикла снова печатается главное меню. ---------------------------------------------------------- ИМЯ: office ---------------------------------------------------------- office Делопроизводитель НАЗНАЧЕНИЕ Обеспечивает простой доступ ко многим утилитам управления делоп- роизводством, которые мы уже рассмотрели. ФОРМАТ ВЫЗОВА office ПРИМЕР ВЫЗОВА office Проверяет мой почтовый ящик и сообщает, пуст ли он m ТЕКСТ ПРОГРАММЫ office 1 : 2 # @(#) office v1.0 Office Manager Author: Russ Sage 2а Делопроизводитель 3 4 if [ $# -gt 0 ] 5 then echo "office: argument error" >&2 6 echo "usage: office" >&2 7 exit 1 8 fi 9 10 while : 11 do 12 c 13 set `date` 14 echo " 15 $1, $2 $3 $4 16 17 Office Menu 18 ----------- 19 Mail 20 News 21 Calendar 22 Phone 23 Automatic Reminders 24 Shell Command 25 to exit 26 27 press m,n,c,p,a,s or : \c" 28 29 read CMD 30 31 case $CMD in 32 "") exit;; 33 m|M) if [ -s /usr/spool/mail/$LOGNAME ] 34 then echo 35 ll /usr/spool/mail/$LOGNAME 36 echo "\nWould yuo like to see it ( y/n): \c" 37 read CMD 38 if [ "$CMD" = "y" ] 39 then echo 40 mail 41 fi 42 else echo "\nNo mail today" 43 fi 44 echo "\nhit \c" 45 read CMD;; 46 n|N) PWD=`pwd` 47 cd /usr/news 48 echo "\nThe following files are news item in /usr/news:\n" 49 lc 50 echo "\nEnter filename or to exit: \c" 51 read NAME 52 while [ "$NAME" != "" ] 53 do 54 if [ -s $NAME ] 55 then echo "\n-------------------" 56 cat $NAME 57 echo "---------------------" 58 else echo "$NAME is not a news file" 59 fi 60 echo "\nEnter filename or to exit: \c" 61 read NAME 62 done 63 cd $PWD;; 64 c|C) echo "\n" 65 today 66 if [ -s $HOME/calendar ] 67 then echo "Calendar file:\n`ll $HOME/ calendar`" 68 echo "\nCalendar notifications:" 69 PWD=`pwd` 70 cd $HOME 71 calendar 72 cd $PWD 73 echo "\nCheck your mail for calendar notifications" 74 else echo "\nYou do not have a calendar file at $HOME" 75 fi 76 echo "\nhit \c" 77 read CMD;; 78 p|P) phone;; 79 a|A) greet 80 $HB/at 11:45 echo ^GLunch in 15 minutes 81 $HB/at 16:45 echo ^GShift change in 15 minutes 82 83 echo "\nYou will receive notices at 11:45 & 4:45" 84 echo "\nWould you like to leave some reminders (y/n): \c" 85 read CMD 86 if [ "$CMD" = "y" ] 87 then echo "\nThe syntax is: at HR:MN executable_phrase" 88 echo "The time now : `date '+%T'`" 89 echo "\n\ncmd ( to exit): \c" 90 read CMD 91 while [ "$CMD" != "" ] 92 do 93 eval $CMD 94 echo "cmd ( to exit ): \c" 95 read CMD 96 done 97 fi;; 98 s|S) echo "\nenter command: \c" 99 read CMD 100 eval $CMD 101 echo "\nhit \c" 102 read CMD;; 103 *) echo "\nInvalid command" 104 echo "\nhit \c" 105 read CMD;; 106 esac 107 done ПЕРЕМЕННЫЕ СРЕДЫ ВЫПОЛНЕНИЯ CMD Содержит различные команды на разных уровнях меню HOME Еще одна экспортируемая переменная, взятая из среды выполнения LOGNAME Shell-переменная, предварительно экспортированная NAME Содержит имя файла с новостями ОПИСАНИЕ ЗАЧЕМ НАМ НУЖЕН office? Многие инструментальные средства в системе UNIX могут выполнять полезные функции, относящиеся к делопроизводству. В силу способа орга- низации UNIX, это отдельные и самостоятельные средства. Для того чтобы узнать о них, необходимо найти все эти средства в справочных руко- водствах, изучить каждое из них и все время помнить, как с ними рабо- тать. Это очень неудобно. Кроме, того, нам необходимо добавить ряд мощных команд к нашему UNIX, включая несколько отдельных управляемых с помощью меню утилит. Каким образом мы можем все это об'единить? Нам необходим механизм, с помощью которого мы можем получить доступ ко всем функциям, имеющим отношение к делопроизводству. Если использовать меню, мы сможем вызывать каждую функцию нажатием одной клавиши. ЧТО ДЕЛАЕТ office? Office представляет собой попытку собрать наиболее часто исполь- зуемые функции делопроизводства в один пакет, управляемый с помощью меню. Тем самым office упрощает вызов этих функций, поскольку вам не обязательно знать, где эти утилиты размещаются и как к ним обращаться. Если у вас есть помощник-секретарь без всякого опыта работы в системе UNIX, вы можете поручить ему выполнение многих из этих функций. Вы мо- жете добавить новые функции и уровни меню, если это необходимо. В office имеются как неотъемлемые общие функции делопроизводства, так и специфические добавки. Данная утилита пытается управлять как основными функциями, так и кругом задач по вашему вкусу. К базовым функциям относятся почта, новости, календарь и база телефонных номе- ров. Добавляемые особенности включают в себя систему напоминаний, основанную на утилите at, и возможность запуска еще одного командного интерпретатора shell, не выходя из программы office. Благодаря всем этим качествам, все находится в одном месте и обеспечивается простота доступа и сопровождения. Главное меню содержит все доступные функции. Оно выглядит так: ----------------------- | | Fri, Jun 20 16:18:23 | | Office Menu | ----------- | Mail (Почта) | News (Новости) | Calendar (Календарь) | Phone (Телефоны) | Automatic Reminders (Автоматические напоминания) | Shell Command (Переход в shell) | to exit (Выход) | | press m,n,c,p,a,s or : | День и дата в левом верхнем углу получены командой UNIX date. Каждый раз при выводе меню печатается новое значение времени. Оно сообщает вам о том, когда вы начали работать с этим меню, и как долго вы работали. Первым вариантом выбора является почта. Здесь ваш почтовый файл проверяется на ненулевой размер. Если размер нулевой (или такой файл отсутствует), выводится сообщение "no mail today" (сегодня почты нет). Если для вас есть почта, то информация из файла почтового ящика распе- чатывается в длинном формате командой ls и выводится запрос о том, хо- тите ли вы прочитать вашу почту. Если вы сказали "yes" (да), выполня- ется обычная команда UNIX mail. Вторым вариантом выбора являются новости. Файлы новостей хранятся в одном каталоге, где каждая порция новостей представляет собой неза- висимый файл. Все порции новостей хранятся в специальном каталоге с именем /usr/news. Office выводит список всех файлов новостей и затем спрашивает, хотите ли вы просмотреть один из них. Если да, введите имя файла - и он распечатается командой more. Мы предполагаем, что у вас есть сетевая связь между пользователями или какое-то другое средство для получения новостей. Третий вариант выбора - календарь. Поскольку с календарем связана разнообразная информация, данная опция подразделяется на различные функции. Сначала выводится полностью календарь на текущий месяц с вы- деленной сегодняшней датой. Это делается с помощью утилиты today, рассмотренной ранее. Затем выполняется обращение к системной утилите работы с календарем. Если в вашем регистрационном каталоге есть файл с именем calendar, то утилита calendar (выполняемая командой cron для просмотра каждого регистрационного каталога) просматривает записи ва- шего календаря. Если какие-либо записи относятся к сегодняшнему или завтрашнему дню, эта утилита посылает их вам по почте. Командный файл office использует несколько иной подход. Сначала ваш файл календаря проверяется на существование и ненулевой размер. Если файл существует, то он выводится на экран в длинном формате, так что вы можете увидеть сам файл и информацию, относящуюся к нему. После этого выполняется функция calendar. При таком вызове на стандартное устройство вывода печатаются все записи, подходящие по времени. Утили- та calendar, вызываемая командой cron, обеспечивает связь только при помощи почты. Если календарного файла нет, то выводится сообщение, ин- формирующее об этом. Следующим вариантом выбора являются телефоны. Для управления ба- зой данных телефонных номеров используется утилита phone, рассмотрен- ная в предыдущем разделе. Поскольку при вызове phone не выполняется никакой предварительной или последующей обработки данных, вы можете перечитать этот раздел, чтобы вспомнить, как работает эта утилита. Пятый вариант выбора - автоматические напоминания. Эта опция предназначена для того, чтобы помогать вам в слежении за важными мо- ментами времени в течение дня. Первым делом она приветствует вас в ду- хе утилиты greet. Затем запускаются в фоновом режиме два командных файла at. Они срабатывают в 11:45 для объявления о ленче и в 16:45 для объявления о конце рабочего дня. Если вы хотите запустить некоторые из ваших собственных командных файлов в фоновом режиме выполнения, от- ветьте "yes" (да) на следующий вопрос. Для напоминания выводится син- таксис утитлиты at, печатается текущее время, и ожидается ваш ввод ко- манд для at. Красиво и просто в применении! (Если в вашей системе име- ется встроенная возможность at, а вы реализовали нашу версию at под другим именем или в другом каталоге, проследите за соответствующими изменениями в тексте командного файла.) Благодаря способу выполнения введенной вами команды at, вы можете ввести любую команду, но не саму at. Цикл сделан так, что он принимает столько команд, сколько вам нужно. Для выхода из цикла введите возврат каретки. Последняя опция предназначена для выполнения любой желаемой ко- манды вне программы office, оставаясь тем не менее в office. Вы даже можете бы запустить еще один shell вне программы office (введя с кла- виатуры sh), а затем по control-D вернуться в office. Нет почти ничего невозможного. Для выхода из office достаточно просто нажать возврат каретки. Тем самым произойдет выход из бесконечного цикла и возврат к предыду- щему shell. ПРИМЕРЫ $ office s sh $ Запуск office и выбор работы с shell. Затем запуск shell-команды с именем sh, т.е. самого shell. При этом вы запускаете новый shell и получаете его символ приглашения. По окончании работы с этим shell введите ^d для выхода из него и возврата к главному меню утилиты office. ПОЯСНЕНИЯ Строки 4-8 выполняют проверку на ошибки. Если вы вызвали office с какими-либо опциями, то вы создали ситуацию ошибки. Строки 10-107 - это один большой цикл while. Его структура подоб- на ранее рассмотренным утилитам, управляемым с помощью меню. Строка 12 очищает экран командой c (см. главу 7). Если вы не хотите читать с за- беганием вперед и включать в текст эту команду, вы можете пока про- пустить эту строку или, если у вас система BSD, использовать команду clear. В строке 13 позиционным параметрам присваивается результат ко- манды date. Строки 14-27 выводят само меню, а строка 29 читает вводимую поль- зователем команду. Строки 31-106 представляют собой оператор case, ко- торый распознает введенную команду и выполняет соответствующие действия. Строка 32 выполняет выход из программы office, если был введен только возврат каретки. Строки 33-45 управляют командой mail. Вначале с помощью команды test проверяется, имеет ли файл с почтовыми сообще- ниями размер больше нуля. Если да, выводится пустая строка и почтовый файл печатается в длинном формате команды ls. Затем вам задается воп- рос о том, хотите ли вы просмотреть почту. Еслы вы вводите "y", вызы- вается команда mail. При любом другом вводе выполняется возврат в главное меню. Строки 46-63 обрабатывают команду новостей. Сначала переменная PWD устанавливается на наш текущий рабочий каталог. Это делается для того, чтобы при переходе командой cd в какое-то другое место, мы смог- ли по окончании обработки вернуться туда, где мы начали работать. За- тем по команде cd мы переходим в каталог /usr/news. Это каталог, в ко- тором хранятся файлы новостей. В системе Berkeley доступен каталог /usr/msgs. Этот каталог очень похож на /usr/news и может использо- ваться вместо него, если у вас есть такое желание. Выводится в виде столбцов список файлов из каталога /usr/news (строка 49), и вам зада- ется вопрос о том, хотите ли вы посмотреть какие-нибудь из этих фай- лов. Если вы вводите возврат каретки, команда test воспринимает это как нулевой ввод и выполняется выход из оператора while. Цикл while выполняется до тех пор, пока значение переменной NAME не равно нулю (строка 52). Введенное имя проверяется на существование. Если такой файл существует, то он распечатывается командой cat, обрам- ленной строками из черточек. Если файл не существует, то выводится сообщение о том, что такого файла новостей нет. Затем вас просят ввести имя другого файла. Этот процесс продолжается в цикле, пока вы не введете только возврат каретки. Когда вы выходите из цикла в строке 62, выполняется команда cd для возврата в тот каталог, откуда вы нача- ли работать. Эта команда cd не оказывает никакого влияния после за- пуска утилиты office. Вы должны указать команду cd вашему регистраци- онному shell. Никакая другая программа, запущенная из порожденного shell, не может выполнить для вас команду cd. Строки 64-77 выполняют функции календаря. Сначала выводятся две пустые строки. Почему две? Потому что обычно команда echo вставляет символ новой строки в конце каждой выводимой строки, а выводимой в данный момент строкой является еще один символ новой строки. Затем в строке 65 запускается программа today. Она печатает изображение кален- даря на месяц. Строка 66 проверяет, имеется ли календарный файл в ва- шем регистрационном каталоге. Если да, он выводится в длинном формате команды ls в строке 67. Печатается также сообщение-заголовок "Calendar Notifications" (календарные извещения). Строка 69 запоминает текущий рабочий каталог переменной PWD. За- тем мы переходим командой cd в каталог $HOME, чтобы находиться в том же каталоге, что и календарный файл. В строке 71 вызывается команда UNIX calendar. Она просматривает календарный файл и выводит все подхо- дящие по времени сообщения. По команде cd мы возвращаемся в наш исход- ный каталог для того, чтобы восстановить порядок. Строка 73 выводит сообщение, чтобы вы проверили вашу почту на наличие календарных за- писей. Как указывалось ранее, команда calendar работает двумя способа- ми. Оператор else в строке 74 выводит вам сообщение, если у вас нет календарного файла. По завершении оператора if, в строках 76 и 77 пе- чатается сообщение о том, что нужно нажать возврат каретки, и выполня- ется оператор read. Это блокирующее чтение, которое означает остановку и ожидание какого-либо ввода. Это противоположность неблокирующему чтению, или более широко известной технике чтения с опросом. Опрос возможен в программах, написанных на Си, но запуск опроса в shell мо- жет потребовать немного больших усилий. Для изучения чтения с опросом ознакомьтесь с главой, описывающей терминальные устройства. Строка 78 управляет командой phone. Она вызывает утилиту phone, которую мы рассмотрели ранее, после чего управление передается в конец цикла while. Печатается главное меню, и все начинается сначала. Строки 79-97 управляют автоматическими напоминаниями. Сначала за- пускается программа greet. Она была описана ранее в этой главе. Затем в строках 80 и 81 в режиме фонового выполнения вызываются два команд- ных файла at, которые выполнятся позже в течение дня. В строке 84 вам задается вопрос, желаете ли вы оставить какие-то свои напоминания. Если вы отвечаете "y", выполняются строки 87-96. Если вы не ввели "y", оператор else не выполняется, поэтому управление передается главному меню. Вы видите, что эта возможность предполагает однократное исполь- зование в течение дня. Строки 87-89 выводят некоторую информацию, необходимую для того, чтобы вы завели для себя напоминания. Выводится синтаксис программы at, текущая дата (только время) и затем приглашение вводить вашу ко- манду. Строка 90 читает вашу команду. Строка 91 означает, что если был введен не только возврат каретки, то нужно выполнить этот цикл. Поэто- му цикл while продолжается, пока вы не нажмете только возврат каретки. Строка 93 пытается выполнить команду, которую вы ввели. Команда eval используется для того, чтобы раскрыть значения тех переменных, которые могли быть не раскрыты ранее. Строка 94 просит ввести следующую коман- ду, а строка 95 читает ее. Поскольку чтение стоит в самом конце цикла, оператор while выполняет проверку нового ввода с клавиатуры. Строки 98-102 управляют выходом в shell. Эта часть программы поч- ти идентична циклу, использованному для напоминаний. Нам выдается приглашение на ввод команды, она зачитывается в переменную, затем де- лается синтаксический разбор этой переменной с помощью команды eval. Если вы хотите перейти в shell, выполните команду sh - и она породит shell. Строки 101 и 102 не дают появиться на экране главному меню до тех пор, пока вы не нажмете возврат каретки. Строки 103-105 выполняют проверку на ошибки ввода. Любая непра- вильная команда попадает сюда. Выводится сообщение об ошибке и выда- ется приглашение "hit " (нажмите возврат каретки).