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] В C++ аналогичное различие существует между потоками cout и cerr . Манипулятор endl добавляет в конец потока символ новой строки и вызывает "выталкивание" буфера. Если состояние буфера временно менять не нужно (из соображений производительности, например), воспользуйтесь вместо манипулятора константой '\n' .
Указанная особенность потока stdoutможет приводить к неожиданным результатам. Например, в следующем цикле точка не выводится каждую секунду. Вместо этого все символы сначала помещаются в буфер, а затем целая их группа одновременно выводится на экран, когда буфер оказывается заполненным.
while (1) {
printf(".");
sleep(1);
}
А в этом цикле происходит то, что нам нужно:
while (1) {
fprintf(stderr, ".");
sleep(1);
}
2.1.5. Коды завершения программы
Когда программа завершает работу, она уведомляет операционную систему о своем состоянии, посылая ей код завершения, который представляет собой 16-разрядное целое число. По существующему соглашению нулевой код свидетельствует об успешном завершении, а ненулевой указывает на наличие ошибки. Некоторые программы возвращают различные ненулевые коды, обозначая разные ситуации.
В большинстве интерпретаторов команд код завершения последней выполненной программы содержится в специальной переменной $?. В показанном ниже примере программа lsвызывается дважды, и оба раза запрашивается код ее завершения. В первом случае программа завершается корректно и возвращает нулевой код, во втором случае она сталкивается с ошибкой (указанный в командной строке файл не найден), поэтому код завершения оказывается ненулевым:
% ls /
bin coda etc lib misc nfs proc sbin usr
boot dev home lost+found mnt opt root tmp var
% echo $?
0
% ls bogusfile
ls: bogusfile: No such file or directory
% echo $?
1
Программа, написанная на языке С или C++, указывает код завершения в операторе returnв функции main(). Есть и другие методы задания кодов завершения. Они обсуждаются в главе 3, "Процессы". Например, программе назначается определенный код, когда она завершается аварийно (вследствие получения сигнала).
Читать дальше