строка компилятора, описанная в главе 10.
ТЕКСТ ПРОГРАММЫ b
1 :
2 # @(#) b v1.0 Background task handler Author: Russ Sage
2а Обработчик фоновых задач
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а Своевременное приветствие с терминала
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