Стандартный поток stderr
присваивается программе тем же способом, что и stdin
и stdout
. Информация, записанная в stderr
, оказывается на терминале пользователя даже при изменении назначения стандартного выходного потока. Vis
пишет свою диагностику в stderr
вместо stdout
, так что если один из файлов по каким-то причинам недоступен, сообщение найдет путь на терминал пользователя, а не исчезнет в программном канале или в выходном файле. (Стандартный поток диагностики был изобретен позднее, чем программные каналы: после того, как сообщения об ошибках стали исчезать при передаче через эти каналы.)
Мы решили, отчасти произвольно, что vis
завершается, если не может открыть входной файл; это разумно для программы, чаще всего используемой в режиме диалога и с одним входным файлом. Однако вы можете предложить и другое решение.
Упражнение 6.5.
Напишите программу printable
, которая печатает имя каждого аргумента-файла, содержащего только печатаемые символы; если в файле хранится любой непечатаемый символ, имя не печатается. printable полезна в ситуациях, подобных следующей:
$ pr `printable *` | lpr
Добавьте флаг -v
, чтобы изменить смысл проверки на обратный, как в grep
. Что следует делать, если среди аргументов нет имен файлов? Какой код завершения должна передавать printable
при возврате?
6.4 Вывод на экран порциями: программа p
До сих пор мы использовали cat для просмотра файлов. Но если файл длинный, а связь с системой высокоскоростная, cat выдает выходной файл слишком быстро, что затрудняет его чтение, даже если вы успеваете делать это с помощью ctl-s и ctl-q .
Очевидно, нужно иметь программу для печати файла небольшими удобными порциями, но такая программа не является стандартной, возможно, потому, что первоначальная система UNIX была написана в те времена, когда использовались терминалы "твердой копии" (печати на бумаге) и медленные линии связи. Поэтому наш следующий пример программа p
, которая выводит файл по частям, занимающим полный экран, за один раз, ожидая ответа от пользователя после выдачи каждой порции прежде, чем начать очередной вывод (краткое имя p
весьма удобно для программы, которая часто используется). Подобно другим программам, p
читает или из файлов, поименованных, как аргументы, или из стандартного входного потока:
$ p vis.с
...
$ grep '#define' *.[ch] | p
...
$
Эту программу легче всего писать на Си; стандартные средства неудобны, когда происходит смешанный ввод из файла или конвейера и с терминала. Решение состоит в том, чтобы печатать входной поток небольшими порциями. Удобный размер порции 22 строки, что составляет немногим меньше, чем размер в 24 строки на большинстве видеотерминалов, и одну треть стандартной страницы в 66 строк. Простой способ подсказки пользователю не печатать последний символ перевода строки каждой порции. Курсор остановится на правом конце строки, а не на левой границе (новой строки). При нажатии клавиши RETURN выполняется перевод строки, и следующая строка появляется в нужном месте. Если пользователь печатает ctl-d или q в конце экрана, выполнение программы p
заканчивается.
Мы не станем принимать специальных мер для вывода длинных строк. Мы также не будем беспокоиться о множестве файлов: просто перейдем от одного к другому без примечаний. Следующая команда
$ p имена файлов...
по своему действию аналогична команде
$ cat имена файлов... | p
Если нужны имена файлов, их можно добавить циклом for
:
$ for i in имена файлов
> do
> echo $i:
> cat $i
> done | p
В самом деле, у нас оказывается слишком много средств, которые мы можем внести в программу. Лучше сделать "неоснащенную" версию, а затем развивать ее, как подскажет опыт. Иными словами, необходимые средства это те, которые действительно вам нужны, а не те, которые, по нашему мнению, вы хотели бы иметь.
Структура p
аналогична структуре vis
: основная процедура выполняет цикл по файлам, вызывая функцию print
, выполняющуюся с каждым файлом:
/* p: print input in chunks (version 1) */
#include
#define PAGESIZE 22
char *progname; /* program name for error message */
main(argc, argv)
int argc;
char *argv[];
{
int i;
FILE *fp, *efopen();
progname = argv[0];
if (argc ==1)
Читать дальше
Конец ознакомительного отрывка
Купить книгу