$ cat watchwho
# watchwho: watch who logs in and out
PATH=/bin:/usr/bin
new=/tmp/wwho1.$$
old=/tmp/wwho2.$$
> $old # create an empty file
while : # loop forever
do
who >$new
diff $old $new
mv $new $old
sleep 60
done | awk '/>/ { $1 = "in: "; print }
/</ { $1 = "out: "; print }'
$
только обрабатывает свои аргументы и возвращает код "истина". Мы могли бы заменить ее командой
true
, просто передающей код завершения "истина" (есть также команда
false
), но команда
':'
более эффективна, поскольку не нужно выполнять эту команду, выбирая ее из файловой системы.
В выводе команды
diff
используются символы
<
и
>
для разделения данных из двух файлов. Программа, написанная на языке
awk
, обрабатывает результаты, чтобы сообщить об изменениях в более понятном формате. Обратите внимание на то, что весь цикл передает результаты работы по конвейеру
awk
программе, вместо того, чтобы запускать заново
awk
программу каждую минуту. Для такой обработки редактор
sed
не подходит, поскольку его вывод всегда задерживается по сравнению с входным потоком на одну строку: всегда есть одна входная строка, которая уже обработана, но не напечатана, а это приводит к ненужной задержке.
Поскольку файл
old
создается пустым, первый вывод команды
watchfor
содержит весь список пользователей, находящихся в системе в данный момент. Замена команды, которая создает файл
old
, на
who > $old
приведет к тому, что
watchfor
выдаст только изменения, но это уже — дело вкуса.
Другая программа в цикле следит за содержимым вашего почтового ящика: как только оно изменяется, программа выдает сообщение: "You have a mail" ("У вас есть почта"). Такая программа является полезной альтернативой встроенному в интерпретатор механизму, использующему переменную
MAIL
. Чтобы показать другой стиль программирования, мы реализовали ее с помощью переменных интерпретатора, а не файлов:
$ cat checkmail
# checkmail: watch mailbox for growth
PATH=/bin:/usr/bin
MAIL=/usr/spool/mail/`getname` # system dependent
t=${1-60}
x="`ls -l $MAIL`"
while :
do
y="`ls -l $MAIL`"
echo $x $y
x="$y"
sleep $t
done | awk '$4 < $12 { print "You have mail" }'
$
Мы опять воспользовались
awk
программой, на этот раз — чтобы добиться вывода сообщения только в тех случаях, когда почтовый ящик пополняется, а не просто изменяется. Иначе вы получите сообщение сразу после исключения письма. (Версия, встроенная в интерпретатор, имеет такой недостаток.)
Обычно интервал времени устанавливается равным 60 с, но если командная строка содержит параметр, например
$ chekmail 30
то интервал задается им. Переменная интерпретатора принимает в качестве значения заданное параметрами время или 60 с, если время не задано, с помощью присваивания
t=${1-60}
Это еще одна возможность языка
shell
.
${var}
эквивалентно
$var
и может использоваться для преодоления трудностей, связанных с появлением переменных внутри буквенно-цифровых строк:
$ var=hello
$ varx=goodbye
$ echo $var
hello
$ echo ${var}x
hellox
$
Определенные символы внутри фигурных скобок задают специальную обработку переменной. В том случае, когда переменная не определена и за ее именем идет знак вопроса, выдается строка, следующая за символом
?
, и интерпретатор прекращает работу (если только он не работает в диалоговом режиме). При отсутствии строки печатается стандартное сообщение:
$ echo ${var?}
hello
все в порядке, var определено
$ echo ${junk}
junk: parameter not set
стандартное сообщение
$ echo ${junk?error!}
junk: error!
строка задана
$
Отметим, что в сообщении, выдаваемом интерпретатором, всегда указывается имя неопределенной переменной.
В другой конструкции
${var-thing}
выбирается
$var
, если оно определено, и
thing
— в противном случае. В подобной конструкции
${var-thing}
значение
$var
также устанавливается равным
thing
:
$ echo ${junk-'Hi there'}
Hi there
$ echo ${junk?)
junk: parameter not set
значение junk не изменилось
$ echo {junk='Hi there'}
Hi there
$ echo ${junk?}
Hi there
junk принял значение Hi there