Дело в том, что контроль ошибок весьма утомителен, но тем не менее важен. Из-за ограниченного объема книги и обширности излагаемого в ней материала мы не уделяли должного внимания этому вопросу. Но в настоящих, "производственных" программах не следует позволять себе игнорировать ошибки.
Упражнение 7.10
Модифицируйте
checkmail
так, чтобы идентифицировать посылающего сообщение: "У вас есть почта". Подсказка:
sscanf
,
lseek
.
Упражнение 7.11
Модифицируйте
checkmail
так, чтобы она не переходила к каталогу почты перед входом в цикл. Окажет ли это ощутимое влияние на ее производительность? Более трудный вопрос: можете ли вы написать версию
checkmail
, которая обходится только одним процессом для оповещения всех пользователей?
Упражнение 7.12
Напишите программу
watchfile
, которая управляет файлом и печатает его с начала всякий раз, как он изменится. Когда бы вы ее использовали?
Упражнение 7.13
Программа
sv
очень "прямолинейна" при обработке ошибок. Модифицируйте ее так, чтобы она продолжала выполняться, даже если не удается обработать некоторый файл.
Упражнение 7.14
Сделайте
sv
рекурсивной: если один из исходных файлов — каталог, то этот каталог и все его файлы обрабатываются таким же образом. Сделайте рекурсивной
cp
. Подумайте, следует ли
cp
и
sv
объединить в одну программу, чтобы
cp -v
не создавала копию, если целевой файл новее файла-источника.
Упражнение 7.15
Напишите программу
random
.
$ random filename
должна выдавать одну строку, произвольно выбранную из файла. Если есть файл
people
, содержащий имена,
random
можно использовать в программе
scapegoat
("козел отпущения"), полезной при случайном определении виновных:
$ cat scapegoat
echo "В этом виноват `random people`!"
$ scapegoat
В этом виноват Кен!
$
Убедитесь в том, что
random
хороша независимо от распределения длины строк.
Упражнение 7.16
Помимо прочего в индексном дескрипторе указаны адреса размещения блоков файла на диске. Рассмотрите файл
<sys/into.h>
, а затем напишите программу
icat
, которая должна читать файлы, описываемые номером записи каталога и устройством диска. (Она, конечно, будет работать только в том случае, если требуемый диск открыт на чтение.) При каких обстоятельствах
icat
полезна?
7.4 Процессы
В этом разделе мы покажем вам, как выполнить одну программу, вызвав ее из другой. Самый легкий путь — привлечь стандартную библиотечную программу
system
, упомянутую, но забракованную в гл. 6. Программа
system
использует один аргумент — командную строку в том виде, в каком она вводится с терминала (за исключением символа перевода строки), и выполняет ее порожденным
shell
. Если командная строка должна быть создана из кусочков, можно прибегнуть к форматированию памяти программой
sprintf
.
В конце раздела мы рассмотрим более безопасную версию
system
для работы с диалоговыми программами, но прежде чем изучать программу в целом, обсудим структуры, из которых она составляется.
Создание процесса низкого уровня:
execlp
и
execvp
Самая важная операция - выполнение другой программы без возврата с помощью системного вызова
execlp
. Например, чтобы напечатать дату и выполнить тем самым последнее действие запущенной программы, используют
execlp("date", "date", (char*)0);
Первый аргумент
execlp
есть имя файла команды;
execlp
выбирает путь поиска (т.е.
$PATH
) из вашего окружения и выполняет такой же поиск, как
shell
. Второй и последующие аргументы — это имена и аргументы команд; для новой программы они становятся массивом
argv
. Конец списка отмечен аргументом 0. (См. справочное руководство по
exec(2)
, и вы поймете конструкцию
execlp
.)
Вызов
execlp
перекрывает существующую программу новой, запускает ее и затем завершается. Первоначальная программа получает управление обратно только при возникновении ошибки, например, когда файл не удается найти или он является невыполнимым:
execlp("date", "date", (char*)0);
fprintf(stderr, "Не удалось выполнить 'date'\n");
exit(1);
Если число аргументов вам заранее не известно, полезно применить
execvp
(вариант
execlp
). Вызов выглядит так:
execvp(filename, argp);
где
argp
означает массив указателей к аргументам (таким, как
argv
). Последним в массиве должен быть указатель
NULL
, так что
execvp
может отметить конец списка. Как и для
execlp
,
filename
— это файл, в котором находится программа,
argp
— массив
argv
для новой программы, a
argp[0]
— имя программы.
Ни одна из перечисленных выше программ не обеспечивает расширения в списке аргументов метасимволов типа
<
,
>
,
*
, кавычки и т.п. Если они вам нужны, воспользуйтесь
execlp
и вызовите
/bin/sh
из
shell
, которая выполнит эту работу. Сконструируйте строку
commandline
, содержащую полную команду, как если бы она была напечатана на терминале, например: