Страница:
с помощью переадресации в файловый дескриптор 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а Обработчик фоновых задач
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
Вы могли бы вставить это в ваш .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а Сообщает время последней регистрации
4 if [ $# -gt 1 ]
5 then echo "lastlog: arg error" >&2
6 echo "usage: lastlog [-l]" >&2
7 exit 1
8 fi
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, который
показывает, был ли это вызов для записи нового значения времени или
для печати старых значений.
Переменная интерпретатора 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а Обработчик фоновых задач
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
Вы могли бы вставить это в ваш .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а Сообщает время последней регистрации
4 if [ $# -gt 1 ]
5 then echo "lastlog: arg error" >&2
6 echo "usage: lastlog [-l]" >&2
7 exit 1
8 fi
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, который
показывает, был ли это вызов для записи нового значения времени или
для печати старых значений.