/* Записываем информацию об использовании программы в поток
stderr и завершаем работу с выдачей кода 1
(аварийное завершение). */
print_usage(stderr, 1);
case -1: /* Опций больше нет. */
break;
default: /*
Какой-то непредвиденный результат. */
abort;
}
}
while (next_option != -1);
/* Обработка опций завершена, переменная OPTIND указывает на
первый аргумент, не являющийся опцией. В демонстрационных
целях отображаем эти аргументы, если задан режим VERBOSE. */
if (verbose) {
int i;
for (i = optind; i < argc; ++i)
printf("Argument: %s\n", argv[i]);
}
/* Далее идет основное тело программы... */
return 0;
}
Может показаться, что использование функции
getopt_long
приводит к написанию громоздкого кода, но, поверьте, самостоятельный синтаксический анализ опций командной строки — гораздо более трудоемкая задача. Функция
getopt_long
достаточно универсальна и гибка в работе с опциями, но лучше не заниматься сложными вещами. Старайтесь придерживаться традиционной структуры задания опций.
2.1.4. Стандартный ввод-вывод
В стандартной библиотеке языка С определены готовые потоки ввода и вывода (
stdin
и
stdout
соответственно). Они используются функциями
scanf
,
printf
и целым рядом других библиотечных функций. Согласно идеологии UNIX, стандартные потоки можно перенаправлять. Это позволяет образовывать цепочки программ, связанных посредством каналов (конкретный синтаксис перенаправления потоков и работы с каналами можно узнать в документации к интерпретатору команд).
Есть также стандартный поток ошибок:
stderr
. Программы должны направлять предупреждающие сообщения и сообщения об ошибках в него, а не в поток
stdout
. Это позволяет отделять обычные выводные данные от разного рода служебных сообщений. К примеру, стандартный поток вывода можно направить в файл, а сообщения об ошибках, по-прежнему отображать на консоли. Запись в поток
stderr
осуществляется с помощью функции
fprintf
:
fprintf(stderr, ("Error: ..."));
Все три стандартных потока доступны низкоуровневым функциям ввода-вывода (
read
,
write
и т.д.) через дескрипторы файлов. В частности, поток
stdin
имеет дескриптор 0,
stdout
— 1, a
stderr
— 2.
При вызове программы иногда требуется одновременно перенаправить потоки вывода и ошибок в файл или канал. Синтаксис подобной операции зависит от используемого интерпретатора команд. В интерпретаторах семейства Bourne shell (включая
bash
,
который по умолчанию установлен в большинстве дистрибутивов Linux) это делается так:
% program > output_file.txt 2>&1
% program 2>&1 | filter
Запись
2>&1
означает, что файл с дескриптором 2 (
stderr
) объединяется с файле имеющим дескриптор 1 (
stdout
). Обратите внимание на то, что эта запись должна идти после операции перенаправления в файл (первый пример), но перед операцией перенаправления в канал (второй пример).
Поток
stdout
является буферизуемым. Записываемые в него данные не посылаются на консоль (или на другое устройство в случае перенаправления), пока буфер не заполнится, программа не завершит работу нормальным способом или файл
stdout
не будет закрыт. Осуществить принудительное "выталкивание" буфера позволяет функция
fflush
:
fflush(stdout);
В то же время поток
stderr
не буферизуется. Записываемые в него данные сразу попадают на консоль. [6]
Указанная особенность потока
stdout
может приводить к неожиданным результатам. Например, в следующем цикле точка не выводится каждую секунду. Вместо этого все символы сначала помещаются в буфер, а затем целая их группа одновременно выводится на экран, когда буфер оказывается заполненным.
while (1) {
6
В C++ аналогичное различие существует между потоками
cout
и
cerr
. Манипулятор
endl
добавляет в конец потока символ новой строки и вызывает "выталкивание" буфера. Если состояние буфера временно менять не нужно (из соображений производительности, например), воспользуйтесь вместо манипулятора константой
'\n'
.
printf(".");
sleep(1);
}
А в этом цикле происходит то, что нам нужно:
while (1) {
fprintf(stderr, ".");
sleep(1);
}
2.1.5. Коды завершения программы
Когда программа завершает работу, она уведомляет операционную систему о своем состоянии, посылая ей код завершения, который представляет собой 16-разрядное целое число. По существующему соглашению нулевой код свидетельствует об успешном завершении, а ненулевой указывает на наличие ошибки. Некоторые программы возвращают различные ненулевые коды, обозначая разные ситуации.
В большинстве интерпретаторов команд код завершения последней выполненной программы содержится в специальной переменной
$?
. В показанном ниже примере программа
ls
вызывается дважды, и оба раза запрашивается код ее завершения. В первом случае программа завершается корректно и возвращает нулевой код, во втором случае она сталкивается с ошибкой (указанный в командной строке файл не найден), поэтому код завершения оказывается ненулевым: