$*
и $@
раскрываются как аргументы и снова распознаются; наличие пробелов в аргументах приводит к разбиению их на несколько аргументов;• "
$*
" является единым словом, которое образовано из всех аргументов командного файла, объединенных вместе с пробелами;• «
$*
» идентично аргументам, получаемым командным файлом: пробелы в аргументах игнорируются, в результате получается список слов, идентичных исходным аргументам.Если команда
pick
не имеет аргументов, она, по-видимому, должна читать стандартный входной поток, поэтому можно задать$ pick < mailinglist
вместо
$ pick `cat mailinglist`
Но мы не будем исследовать эту версию команды
pick
во избежание некоторых неприятных осложнений. Кроме того, значительно проще написать такую же программу на Си. С ней вы познакомитесь в следующей главе.Первые два из приведенных ниже упражнений достаточно сложны, но полезны даже для опытных программистов, работающих на языке
shell
.Упражнение 5.24Попробуйте написать программу
pick
, которая читает аргументы из стандартного входного потока, если ничего не задано в командной строке. Она должна правильно обрабатывать пробелы. Будет ли допустим ответ q
? Если нет, то попытайтесь выполнить следующее упражнение.Упражнение 5.25Хотя встроенные команды интерпретатора, такие, как
read
и set
, нельзя переключить, можно временно переключить сам интерпретатор. Прочтите в справочном руководстве раздел по sh(1)
, в котором описывается команда exec
, и придумайте, как читать из /dev/tty
без вызова порожденного интерпретатора. (Может оказаться полезным сначала прочитать гл. 7.)Упражнение 5.26(Более простое.) Используйте команду
read
в вашем файле .profile
для инициации TERM
, а также всего, что зависит от нее, например позиции табуляции.5.8 Команда news
: служба информации пользователей
В гл. 1 упоминалось о том, что в вашей системе может быть команда
news
для передачи сообщений, представляющих интерес для всех пользователей системы. Хотя названия команды и ее детали могут различаться, большинство систем имеет службу информации. Мы рассматриваем команду news
не для замены вашей местной команды, а чтобы показать, как легко написать такую программу на языке shell
. Неплохо было бы сравнить реализацию предлагаемой здесь команды news с вашей версией.Обычно основная идея таких программ заключается в том, что отдельные фрагменты новостей хранятся по одному в файлах в специальном каталоге типа
/usr/news
. Наша команда news
сравнивает время изменения файлов в каталоге /usr/news
и вашем исходном каталоге (.news_time
). В целях отладки мы можем использовать каталог '.'
как для файлов новостей, так и для news_time
. Можно заменить его на /usr/news
, когда программа будет готова для общего пользования:$ cat news
# news: print news files, version 1
HOME=. # debugging only
cd . # place holder for /usr/news
for i in `ls -t * $HOME/.news_time`
do
case $i in
*/.news_time) break ;;
*) echo news: $i
esac
done
touch $HOME/.news_time
$ touch .news-time
$ touch x
$ touch y
$ news
news: y
news: x
$
Команда
touch
заменяет время последней модификации файла, заданного в качестве аргумента, на настоящее время, не подвергая сам файл модификации. Для отладки мы даем только эхо имен файлов новостей, а не печатаем их. Цикл завершается при обнаружении news_time
, тем самым перечисляются только файлы со свежими новостями. Заметьте, что символ *
в операторе case
может быть сопоставлен с /
, что недопустимо для шаблонов имен файлов. А что будет, если news_time
не существует?$ rm .news_time
$ news
$
Отсутствие ответа удивляет и является ошибочным. Это вызвано тем, что когда команда
ls
не находит файл, она выдает соответствующее сообщение в стандартный выходной поток прежде, чем вывести какую-либо информацию о существующих файлах. Такая ситуация, безусловно, ошибочна — диагностические сообщения должны передаваться в стандартный файл диагностики. Но мы можем обнаружить эту ситуацию в цикле и переключить стандартный файл диагностики на стандартный выходной поток, так что все версии будут работать одинаково. (Данная проблема решена в новой версии, но мы рассмотрели ее, чтобы проиллюстрировать, как легко устранить недоделки.)$ cat news
# news: print news files, version 2
HOME=. # debugging only
cd . # place holder for /usr/news
IFS='
' # just a newline
for i in `ls -t * $HOME/.news_time 2>&1`
do
case $i in
*' not found') ;;
*/.news_time) break ;;
*) echo news: $i ;;
esac
done
touch $HOME/.news_time
$ news
news: news
news: y
news: x
$
Мы должны были установить
IFS
равным символу конца строки, чтобы сообщение./.news_time not found
не распознавалось как три слова.
Команда
news
должна выводить на печать файлы новостей, а не создавать эхо их имен. Полезно знать, кто и когда послал сообщение, поэтому мы воспользуемся командами set
и ls -l
для вывода заголовка перед самим сообщением:$ ls -l news
-rwxrwxrwx 1 you 208 Oct 1 12:05 news
$ set `ls -l news`
-rwxrwxrwx: bad option(s)
Что-то неправильно!$
Это один из тех случаев, когда взаимозаменяемость программы и данных на языке
shell
имеет значение. Команда set
"ругается", потому что ее аргумент ("-rwxrwxrwx
") начинается с минуса и, следовательно, выглядит как флаг. Очевидным (хотя и неэлегантным) решением было бы предварить аргумент обычным символом:$ set X`ls -l news`
$ echo "news: ($3) $5 $6 $7"
news: (you) Oct 1 12:05
$
Здесь представлен разумный формат с указанием автора и даты сообщения вместе с именем файла. Приведем окончательный вариант команды
news
:# news: print news files, final version
PATH=/bin:/usr/bin
IFS='
' # just a newline
cd /usr/news
for i in `ls -t * $HOME/.news_time 2