Главная » Программирование для UNIX » Иллюстрация к обработке ошибок: sv

0

Теперь  напишем программу под названием  sv (похожую на cp), которая  копирует набор  файлов в  каталог, но  при  этом  перезаписывает файл назначения, только если  он не  существует  или  является  более

старым, чем исходный файл. Название sv происходит от английского слова save (сохранить) – идея  в том, что sv не перезаписывает более новые версии файлов. Команда sv учитывает больше информации из индексного дескриптора, чем checkmail.

Будем запускать sv следующим образом:

$ sv  file1 file2 … dir

Эта команда должна копировать file1 в dir/file1, file2 в dir/file2 и т. д., за исключением тех случаев, когда файл назначения новее соответствующего исходного файла – в этом случае копия не делается и выдается предупреждение. Для  того чтобы  избежать многократного копирования ссылок, программа sv не разрешает использовать / в именах исходных файлов.

/*  sv:  сохраняет новые файлы  */

#include <stdio.h>

#include <sys/types.h>

#include <sys/dir.h>

#include  <sys/stat.h> char  *progname;

main(argc,  argv) int  argc;

char  *argv[];

{

int i;

struct stat stbuf;

char  *dir  = argv[argc–1];

progname  =  argv[0]; if  (argc  <=  2)

error("Usage: %s  files… dir", progname);

if (stat(dir, &stbuf)  == –1)

error("can’t  access  directory  %s", dir); if  ((stbuf.st_mode  &  S_IFMT)  !=  S_IFDIR)

error("%s  is  not  a  directory",  dir); for  (i =  1;  i < argc–1;  i++)

sv(argv[i],  dir); exit(0);

}

Значения времени в индексном дескрипторе задаются в секундах после 0:00  GMT, 1 января 1970, так  что более старые файлы имеют  меньшие значения в поле st_mtime.

sv(file, dir)      /*  сохраняет file  в dir  */ char  *file, *dir;

{

struct  stat  sti,  sto; int  fin,  fout,  n;

char  target[BUFSIZ],  buf[BUFSIZ],  *index(); sprintf(target,  "%s/%s", dir,  file);

if (index(file, ‘/’) !=  NULL)    /*  strchr()  в некоторых  системах */ error("won’t  handle  /’s in  %s",  file);

if (stat(file,  &sti) == –1)

error("can’t stat %s", file);

if (stat(target, &sto)  == –1)      /*  файл  назначения не существует */ sto.st_mtime = 0;      /*  пусть он будет  старее  */

if (sti.st_mtime  < sto.st_mtime)      /*  файл  назначения более новый  */

fprintf(stderr,  "%s:  %s  not  copied\n", progname,  file);

else if ((fin  =  open(file,  0))  ==  –1) error("can’t  open file  %s",  file);

else if ((fout  =  creat(target,  sti.st_mode)) == –1) error("can’t create  %s",  target);

else

while ((n  =  read(fin, buf,  sizeof buf))  >  0) if  (write(fout,  buf,  n)  !=  n)

error("error  writing  %s",  target);

close(fin); close(fout);

}

Вместо стандартных функций ввода-вывода использована creat для того, чтобы sv имела возможность сохранить код прав  доступа исходного файла. (Обратите внимание на то, что index и strchr – это разные названия одной  и  той же  процедуры; посмотрите в руководстве  описание string(3), чтобы узнать, какое имя использует ваша система.)

Несмотря на то что sv  – это специализированная программа, она  иллюстрирует несколько важных идей. Есть множество программ, хотя и не являющихся «системными», но тем не менее способных использовать информацию, хранимую операционной системой, к которой мож но получить доступ  посредством системных вызовов. Для таких программ ключевым моментом является то, что системные типы данных определены только в стандартных  заголовочных файлах, таких как stat.h и dir.h, и программа включает в себя эти файлы, а не использует собственные определения типов. У такого кода гораздо больше шансов оказаться переносимым из одной системы в другую.

Не удивляйтесь, что как минимум две трети кода программы sv  – это проверка ошибок. На  начальных этапах написания программы есть соблазн сэкономить на обработке ошибок, так как это не входит в постановку основной  задачи. А когда программа «заработает», уже  не хватает энтузиазма для  того,  чтобы  вернуться назад и вставить проверки,  которые  превратят частную программу в  устойчиво работающий  продукт.

Программа sv не защищена от всех  возможных несчастий, например она не обрабатывает прерывания в неудобное время, но она более осто-

рожна, чем большинство программ. Остановимся буквально на одном  моменте – рассмотрим последний оператор write. Редко случается, что write не выполняется успешно, поэтому многие программы игнорируют такую возможность. Но на дисках заканчивается свободное  место;  пользователи превышают  квоты;  происходят обрывы линий  связи. Все эти  обстоятельства могут  послужить причиной ошибок записи, и будет  гораздо лучше, если  пользователь узнает об этом, вместо  того чтобы программа молча делала вид,  что все хорошо.

Мораль в том,  что проверка ошибок скучна и утомительна, но чрезвычайно важна. Из-за ограниченного пространства и желания сконцентрировать внимание на наиболее интересных вопросах авторы опускали  эту часть во многих программах, рассмотренных в данной книге. Но в настоящих программных продуктах вы не можете позволить себе игнорировать ошибки.

Упражнение 7.10.  Измените checkmail  таким  образом,  чтобы  идентифицировать отправителя письма в сообщении You  have  mail (Вы полу чили письмо). Подсказка: sscanf, lseek. ~

Упражнение 7.11.  Измените checkmail так, чтобы каталог не изменялся на почтовый до входа  в цикл. Будет ли это иметь значение для производительности? (Сложнее.) Можете ли вы написать версию checkma– il, которой требовался бы только один  процесс для оповещения всех  пользователей? ~

Упражнение 7.12.  Напишите программу watchfile, которая проверяет файл и печатает  его с начала каждый раз, когда он изменяется.  Где можно использовать такую программу? ~

Упражнение 7.13.   Программа sv  не  обладает  гибкостью в  обработке ошибок. Измените ее так, чтобы  она продолжалась даже в том случае, когда она не может обработать некоторые файлы. ~

Упражнение 7.14.  Сделайте sv рекурсивной:  если  один  из  исходных файлов – это каталог, пусть  каталог и файлы из него обрабатываются одинаково. Сделайте cp рекурсивной. Подумайте, должны ли  cp и sv быть одной и той же программой, так чтобы cp –v не создавала копию в том случае, когда файл назначения более новый. ~

Упражнение 7.15. Напишите программу random:

$ random имя5файла

которая выводит одну строку, случайным образом выбранную из фай ла.  Если  random обрабатывает файл с именами, то она  может быть  использована в программе scapegoat, которая помогает найти виноватого:

$ cat  scapegoat

echo "It’s all ‘random  people‘’s fault!"

$ scapegoat

It’s all Ken’s  fault!

$

Убедитесь, что random работает правильно вне зависимости от распределения длин  строк. ~

Упражнение 7.16.  В индексном дескрипторе содержится и другая информация, как,  например, адреса на  диске, в которых  расположены блоки файлов. Исследуйте файл sys/ino.h, а потом  напишите программу icat, которая будет читать файлы, определяемые по индексному дескриптору и дисковому устройству. (Естественно, она  будет  работать только при  условии, что  рассматриваемый диск  читаем.) При  каких условиях полезна icat? ~

Источник: Керниган Б., Пайк Р., UNIX. Программное окружение. – Пер. с англ. – СПб: Символ-Плюс, 2003. – 416 с., ил.

По теме:

  • Комментарии