3 что такое стандартный поток вывода
Python — stdin, stdout и stderr
Стандартный ввод — это дескриптор файла, который пользовательская программа читает, чтобы получить информацию от пользователя. Мы передаем ввод на стандартный ввод (stdin).
Стандартный вывод — программа пользователя записывает обычную информацию в этот дескриптор файла. Вывод возвращается через стандартный вывод (stdout).
Стандартная ошибка — программа пользователя записывает информацию об ошибке в этот дескриптор файла. Ошибки возвращаются через стандартную ошибку (stderr).
Python предоставляет нам файловые объекты, представляющие stdin, stdout и stderr. Давайте посмотрим, как мы могли бы работать с этими объектами, чтобы использовать ввод и вывод программы.
1. sys.stdin
Давайте разберемся на простом примере:
Примечание. Обычно мы не закрываем объект файла stdin по умолчанию, хотя это разрешено. Итак, stdin_fileno.close() — допустимый код Python.
2. sys.stdout
3. sys.stderr
Это похоже на sys.stdout потому что он также sys.stdout непосредственно выводит в консоль. Но разница в том, что он печатает только сообщения об исключениях и ошибках. (Вот почему он называется стандартной ошибкой).
Проиллюстрируем это на примере.
Перенаправление в файл
Сначала мы сохраняем исходный sys.stdout обработчик файла sys.stdout в другую переменную. Нам это нужно не только для восстановления sys.stdout в старом обработчике (указывающем на консоль), но мы также можем печатать на консоль, используя эту переменную!
Обратите внимание, что после записи в файл мы закрываем его, аналогично тому, как мы закрываем файл, потому что этот файл все еще был открыт.
Потоки данных
Статья посвящена работой с потоками данных в bash. Я постарался написать ее наиболее доступным и простым языком, чтобы было понятно даже новичкам в Linux.
В одной из моих статей мы рассматривали запись звука в файл с помощью команды:
Эта команда читает файл (устройство) /dev/audio с помощью команды cat и перенаправляет информацию из него в файл /tmp/my.sound (с помощью оператора >).
У каждой программы существует 3 системных потока: stdout, stderr, stdin.
stdout
Стандартный поток вывода данных для программ. Например, когда мы пишем команду ls, то список папок и файлов она выводит именно в этот поток, который отображается у нас в консоли:
stderr
Поток вывода ошибок. Если программа не смогла сделать все как надо — она пишет именно в этот поток. Например, когда rm пытается удалить несуществующий файл:
$ rm example.txt
rm: example.txt: No such file or directory
stdin
Поток ввода данных. А вот это довольно интересный и удобный поток. Например, его использует вэб-сервер, когда просит интерпретаторы выполнить скрипты через CGI. Мы тоже можем попробовать:
В этом примере мы встретили оператор перенаправления потока вывода. Мы остановимся на нем позже.
Перенаправление потоков
Для начала рассмотрим перенаправление потоков в файлы, устройства и другие потоки.
В этом примере мы направили stdout команды ls в файл 1.txt. Читаем его:
Да, все успешно записалось.
Теперь попробуем направить stderr команды rm:
Здесь мы использовали номер потока stderr (2). По умолчанию оператор > перенаправляет поток stdout, который имеет номер 1. Чтобы направить другой поток, надо перед оператором > поставить его номер.
Мы можем направлять одни потоки в направлении других:
В этом примере мы направили поток stdout в файл 1.txt, а затем направили stderr туда же, куда направлен stdout с помощью оператора & перед номером потока.
Теперь давайте поиграем с потоком stdin. Например, я хочу найти все папки «.svn» в некотором проекте и удалить:
Команда find с параметром. выводит в stdout все вложенные папки и файлы, которые находит в данной папке и во всех вложенных.
Теперь нам надо выбрать только папки с именем «.svn»:
Оператор | перенаправляет stdout одного приложения в stdin следующего. То есть все строки найденные с помощью find пошли в команду grep, которая выбирает строки по определенным условиям и выводит их. Здесь условие — это регулярное выражение, которое говорит о том, что строка должна заканчиваться на «/.svn».
Нужные папки мы выбрали, осталось их удалить.
И снова новый оператор: `. Он забирает stdout из команды, которую он окружает и вставляет в данное место как строку.
Получается, что мы запросили все файлы, выбрали из них папки с именем «.svn» и отдали результат как аргументы команде rm. В этом случае у нас будут проблемы если имена файлов и папок содержат пробелы. Исправляем ситуацию:
Буферизированный (потоковый) ввод-вывод
6.1. Стандартные потоки в операционных системах
Принцип «потокового ввода-вывода» следующий:
В буфер заносятся только символьные данные. Как следствие, при потоковом выводе нельзя (или «практически нельзя») изменить атрибуты выводимых символов, их шрифтовое и абзацное оформление и т.д. Кроме того, в стандартные потоки ввода-вывода нельзя выводить «двоичные» файлы (то есть файлы, имеющую кодировку, отличную от ASCII и совместимой с ней кодировки). В операционных системах корпорации Microsoft при потоковом вводе-выводе для вывода на консоль кириллических символов возможна только кодировка OEM 866 (кодовая страница MS-DOS для русскоязычных пользователей). Это в настоящее время резко ограничивает применение потокового ввода-вывода для создания программ в современном программировании. Однако этот подход широко востребован в UNIX и её клонах, работающих в режиме командной строки.
В качестве «стандартных потоков», присутствующих в операционной системе всегда и никогда не удаляемых из оперативной памяти, используются следующие потоки (см. таблицу 6.1):
Примечание: стандартный поток и поток ошибок разделены в связи с тем, что при перенаправлении вывода часто совсем не нужно записывать в результаты работы программы диагностические сообщения. Эти сообщения будут лишними, например, при формировании таблицы базы данных в виде текстового файла;
Все остальные потоки создаются или уничтожаются с помощью функций открытия и закрытия файлов, на период чтения/записи/добавления информации в эти файлы.
6.2. Ввод со стандартного потока ввода
6.2.1. Ввод средствами языка Си
Ввод со стандартного потока в Си осуществляется при помощи следующих функций:
С синтаксисом и правилами использования этих функций можно ознакомиться в приложении №I к данной лекции. Автор хочет отметить, что использование функций ввода со стандартного потока является небезопасным, а, следовательно, и нежелательным способом ввода данных из стандартного потока. По возможности, заменяйте эти функции функциями потокового чтения данных с «явным» указанием потоков и «длины» прочтённой строки.
Стандартные потоки ввода/вывода
Материал из Xgu.ru
Стандартные потоки ввода и вывода в UNIX/Linux наряду с файлами являются одним из наиболее распространённых средств для обмена информацией процессов с внешним миром, а перенаправления >, >> и |, одной из самых популярных конструкций командного интерпретатора.
На этой странице рассматриваются как базовые вопросы использования потоков ввода/вывода, так и тонкости и хитрости, например, почему не работает echo text | read ver и многие другие.
Содержание
[править] Потоки и файлы
Процесс взаимодействия с пользователем выполняется в терминах записи и чтения в файл. То есть вывод на экран представляется как запись в файл, а ввод — как чтение файла. Файл, из которого осуществляется чтение, называется стандартным потоком ввода, а в который осуществляется запись — стандартным потоком вывода.
Стандартные потоки — воображаемые файлы, позволяющие осуществлять взаимодействие с пользователем как чтение и запись в файл. Кроме потоков ввода и вывода, существует еще и стандартный поток ошибок, на который выводятся все сообщения об ошибках и те информативные сообщения о ходе работы программы, которые не могут быть выведены в стандартный поток вывода.
Стандартные потоки привязаны к файловым дескрипторам с номерами 0, 1 и 2.
Вывод данных на экран и чтение их с клавиатуры происходит потому, что по умолчанию стандартные потоки ассоциированы с терминалом пользователя. Это не является обязательным — потоки можно подключать к чему угодно — к файлам, программам и даже устройствам. В командном интерпретаторе bash такая операция называется перенаправлением.
Использовать файл как источник данных для стандартного потока ввода. > файл Направить стандартный поток вывода в файл. Если файл не существует, он будет создан; если существует — перезаписан сверху. 2> файл Направить стандартный поток ошибок в файл. Если файл не существует, он будет создан; если существует — перезаписан сверху. >>файл Направить стандартный поток вывода в файл. Если файл не существует, он будет создан; если существует — данные будут дописаны к нему в конец. 2>>файл Направить стандартный поток ошибок в файл. Если файл не существует, он будет создан; если существует — данные будут дописаны к нему в конец. &>файл или >&файл Направить стандартный поток вывода и стандартный поток ошибок в файл. Другая форма записи: >файл 2>&1. >&- Закрыть поток вывода перед вызовом команды (спасибо [1]); 2>&- Закрыть поток ошибок перед вызовом команды (спасибо [2]); cat
Весь текст между блоками EOF (в общем случае вместо EOF можно использовать любое слово) будет выведен на экран. Важно: перед последним EOF не должно быть пробелов! (heredoc синтаксис).
EOF Аналогично, но только для одной строки (для bash версии 3 и выше)
Пример. Эта команда объединяет три файла: header, body и footer в один файл letter:
Команда cat по очереди выводит содержимое файлов, перечисленных в качестве параметров на стандартный поток вывода. Стандартный поток вывода перенаправлен в файл letter.
Здесь используется сразу перенаправление стандартного потока ввода и стандартного потока вывода:
Программа sort сортирует данные, поступившие в стандартный поток ввода, и выводит их на стандартный поток вывода. Стандартный поток ввода подключен к файлу unsortedlines, а выход записывается в sortedlines.
Здесь перенаправлены потоки вывода и ошибок:
Программа find ищет в каталоге /home файлы с суффиксом .rpm. Список найденных файлов записывается в файл rpmlist. Все сообщения об ошибках удаляются. Удаление достигается при помощи перенаправления потока ошибок на устройство /dev/null — специальный файл, означающий ничто. Данные, отправленные туда, безвозвратно исчезают. Если же прочитать содержимое этого файла, он окажется пуст.
Для того чтобы лучше понять, что потоки работают как файлы, рассмотрим такой пример:
Программа cat запускается для записи данных в файл /tmp/fff. Он запускается в фоне (&), и получает номер работы 1 ([1]). Процесс этой программы имеет номер 28378.
Информация о процессе 28738 находится в каталоге /proc/28738 специальной псевдофайловой системы /proc. В частности, в подкаталоге /proc/28738/fd/ находится список файловых дескрипторов для открытых процессом файлов.
Здесь видно, что стандартный поток ввода (0), и стандартный поток ошибок (2) процесса подключены на терминал, а вот стандартный поток вывода (1) перенаправлен в файл.
Завершить работу программы cat можно командой kill %1.
Командный интерпретатор — это тоже процесс. И у него есть стандартные потоки ввода и вывода. Если интерпретатор работает в интерактивном режиме, то они подключены на консоль (вывода на экран; чтение с клавиатуры). Можно обратиться напрямую к этим потокам изнутри интерпретатора:
Например, здесь видно, что потоки ссылаются в конечном итоге на файл-устройство терминала, с которым работает интерпретатор:
[править] Каналы
Стандартные потоки можно перенаправлять не только в файлы, но и на вход других программ. Если поток вывода одной программы соединить с потоком ввода другой программы, получится конструкция, называемая каналом, конвейером или пайпом (от англ. pipe, труба).
В bash канал выглядит как последовательность команд, отделенных друг от друга символом |:
Стандартный поток вывода команды1 подключается к стандартному потоку ввода команды2, стандартный поток вывода команды2 в свою очередь подключается к потоку ввода команды3 и т.д.
В UNIX/Linux существует целый класс команд, предназначенных для преобразования потоков данных в каналах. Такие программы известны как фильтры. Программа-фильтр читает данные, поступающие со стандартного потока ввода (на вход), преобразовывает их требуемым образом и выводит на стандартный поток вывода (на выход). Существует множество хорошо известных фильтров, призванных решать определенные задачи, и являющихся незаменимым инструментом в руках пользователя ОС.
Каналы в ОС Linux являются одной из наиболее часто применяемых конструкций, а фильтры — наиболее часто применяемых программ. Большинство повседневных задач в Linux легко решаются при помощи конструкций построенных на основе нескольких фильтров.
Программы, образующие канал, выполняются параллельно как независимые процессы.
Можно создавать ответвление в каналах. Команда tee позволяет сохранять данные, передающиеся в канале:
Программа tee копирует данные, поступающие на стандартный поток ввода, в указанные в качестве аргументов команды файлы, и передает данные на стандартный поток вывода.
Рассмотренный ниже пример: сортируется файл unsortedlines и результат записывается в sortedlines.
Команда выполняет те же действия, но запись является более наглядной.
Вот пример посложнее. Вывести название и размер пользовательского каталога, занимающее наибольшее место на диске.
Программа du, при вызове ее с ключом -s, сообщает суммарный объем каждого каталога или файла, перечисленного в ее параметрах.
Ключ -n команды sort означает, что сортировка должна быть арифметической, т.е. строки должны рассматриваться как числа, а не как последовательности символов (Например, 12>5 в то время как строка ’12’ [править] Программы фильтры
В UNIX/Linux существует целый класс команд, которые принимают данные со стандартного потока ввода, каким-то образом обрабатывают их, и выдают результат на стандартный поток вывода. Такие программы называются программами-фильтрами.
Как правило, все эти программы работают как фильтры, если у них нет аргументов (опции могут быть), но как только им в качестве аргумента передаётся файл, они считывают данные из этого файла, а не со стандартного потока ввода (существуют и исключения, например, программа tr, которая обрабатывает данные поступающие исключительно через стандартный поток ввода).
[править] Переменные командного интерпретатора и потоки
Для того чтобы вывести содержимое переменной командного интерпретатора на стандартный поток вывода, используется команда echo:
На стандартный поток ошибок данные можно передать с помощью перенаправления:
Содержимое переменной (или любой другой текст) поступят не на стандартный поток вывода, а на стандартный поток ошибок.
Считать данные со стандартного потока ввода внутрь одной или нескольких переменных можно при помощи команды read:
Строка из стандартного потока ввода считается внутрь переменной VAR.
Если указать несколько переменных, то в первую попадёт первое слово; во вторую — второе слово; в последнюю — всё остальное.
Если прочитать данные со стандартного потока ввода не удалось, то команда read возвращает код завершения отличный от нуля. Это позволяет использовать read, например, в такой конструкции:
В этом примере read считывает строки из файла users, и для каждой прочитанной строки вызывается команда useradd, которая добавляет пользователя в системе. В результате: создаются учётные записи пользователей, имена которых перечислены в файле users.
Переменная user после выхода из цикла остаётся в том же виде, в каком она была до входа в цикл (а не содержит последнее значение файла, как можно было бы предположить). Это связано с тем, что для обработки while, на вход которому направлен канал, порождается дочерний интерпретатор, и модификация переменной происходит внутри него.
[править] Именованные каналы
Безымянный канал можно построить только между процессами, которые порождены от одного процесса (и на практике они должны быть порождены одновременно, а не последовательно, хотя теоретически это не обязательно). Если же процессы имеют разных родителей, то между ними обычный, безымянный канал построить не получится.
Например, в данном случае d и e, и f и g легко могут быть соединены при помощи канала, но e и f соединить с помощью канала не получится.
Для решения этой задачи используются именованные каналы fifo (first in, first out). Они во всём повторяют обычные каналы (pipe), только имеют привязку к файловой системе. Создать именованный канал можно командой mkfifo:
Созданный канал можно использовать для соединения процессов между собой. Например, эти перенаправления будут работать одинаково:
(здесь f и g — процессы из вышеуказанной иерархии процессов). Процессы f и g имеют общего предка. А вот для процессов e и g, не связанных между собой, обычный канал использовать не получится, только именованный:
[править] Потоки ввода/вывода и терминал
Большинство программ, которые работают с потоками ввода и вывода, работают с ними как с простыми файлами, и не рассчитывают на то, что поток подключен к терминалу. Но не все. Если потоки ввода/вывода отключаются от терминала и перенаправляются в файл, часть возможностей программы может пропасть.
Например, если командный интерпретатор отвязать от терминала, то у него потеряется множество интерактивных возможностей:
Например, возможности прокручивать историю команд у него теперь нет. Возможности по редактированию теперь тоже сильно урезаны. Фактически, редактирование команды теперь выполняется уже с помощью программы cat, которая держит терминал, а интерпретатору поступают уже полностью введённые команды.
Проверить, подключен ли наш стандартный поток к терминалу, или он перенаправлен в файл, можно при помощи test, ключ -t:
[править] Подстановка процесса
Есть хитрый трюк, который в чистом виде перенаправлением потока ввода/вывода не является, но имеет к нему отношение — подстановка процесса. Результат выполнения процесса можно представить в виде воображаемого файла и передать его другому процессу.
При таком вызове процессу команда1 передаётся файл (созданный налету канал или файл /dev/fd/. ), в котором находятся данные, которые выводит команда2.
Например, у вас есть два файла с различными словами по одному в строке. Вы хотите определить, какие из них встречаются в одном файле, но не встречаются во втором. И наоборот.
Получается, что в первом файле присутствует слово, которое отсутствует во втором (слово b).
[править] Перенаправление вывода скрипта
Потоки ввода/вывода скрипта, исполняющегося сейчас, изнутри самого скрипта можно следующим образом:
[править] Поменять местами стандартный поток вывода и стандартный поток ошибок
[править] Просмотр прогресса и скорости обработки данных в потоке
Если пропустить данные, передающиеся в канале, через программу pv, то будет видна скорость обработки, время в течение которого работает канал и, если известно сколько данных должно быть обработано, приблизительное время окончания выполнения.
Учебник по языку программирования D. Часть 4
Конкретные понятия, которые представлены в программе, называются переменными. Значение вроде температура воздуха или более сложный объект, как двигатель машины, могут быть переменными в программе.
Каждая переменная имеет определенный тип и определенное значение. Большинство переменных также имеют имена, но некоторые являются анонимными.
В качестве примера переменной мы можем рассматривать концепцию числа учеников в школе. Так как число студентов является целым числом, int — подходящий тип, и studentCount будет достаточно описывающим именем.
Согласно синтаксическим правилам D переменная начинается с ее типа с последующим именем. Введение переменной в программу называется определением. Как только переменная определена, ее имя начинает представлять ее значение.
void main ( )
<
// Определение переменной; это определение
// указывает, что типом studentCount является int:
int studentCount ;
Вывод этой программы:
Как видно из этой строки, значение studentCount равно 0. Согласно таблице фундаментальных типов из прошлой главы: начальное значение int равно 0.
Заметьте, что строка studentCount не появляется в выводе. Другими словами, программа не выводит «Присутствует studentCount учеников».
Значения переменных изменяются с помощью оператора =. Оператор = присваивает новые значения переменным, и по этой причине и называется оператором присвоения:
Когда значение переменной известно в момент определения, значение переменной может быть присвоено одновременно с ее определением. Это важный принцип; это делает невозможным использование переменной до момента присвоения предназначенного ей значения:
void main ( )
<
// Одноврменное определение и присвоение значения:
int studentCount = 100 ;
Упражнения
void main ( )
<
int amount = 20 ;
double rate = 2.11 ;
Стандартные потоки ввода и вывода
До этого момента весь напечатанный вывод наших программ появлялся в окне консоли. Хотя это на самом деле часто используется многими программами, на самом деле символы выводятся в стандартные потоки вывода.
Стандартный вывод основан на символах; все, что печатается, сначала конвертируется в символьное представление и потом последовательно отправляется на вывод как символы. Например, целочисленное значение 100, которое мы выводили в прошлой главе не посылается на вывод как значение 100, а как три символа 1, 0 и 0.
Аналогично то, что мы обычно воспринимаем как клавиатура, на самом деле является стандартным потоком ввода программы, и он также основан на символах. Информация всегда поступает как символы для конвертацию в данные. Для примера целочисленное значение 42 на самом деле приходит через стандартный ввод как символы 4 и 2.
Эти преобразования производятся автоматически.
Это понятие последовательных символов называется символьным потоком. Так как стандартный поток ввода и стандартный поток вывода подходят под это описание, они являются символьными потоками.
Названия стандартных потоков ввода и вывода в D — stdin и stdout соответственно.
Операции над этими потоками обычно требуют имя потока, точку и название операции; например stream.operation(). Так как stdin и stdout используются очень часто, по соглашению, стандартные операции над ними могут быть вызываны без необходимости указывания имени и точки, например operation().
writeln, которую мы использовали в предыдущих главах на самом деле является сокращением от stdout.writeln. Также write — сокращение от stdout.write. Соответственно программа «привет мир» может быть написана так:
void main ( )
<
stdout. writeln ( «Hello world!» ) ;
>
Упражнения
Чтение из стандартного потока ввода
Любые данные, которые были прочитаны программой, должны быть сперва сохранены в переменную. Например, программа, которая читает количество студентов из стандартного потока ввода, должна сохранить эту информацию в переменную. Типом этой конкретной переменной может быть int.
Как мы убедились в предыдущей главе, нет необходимости указывать stdout, когда выводим информацию, так как он предполагается. Далее, то, что выводится, указывается как аргумент. Поэтому write(studentCount) достаточно, чтобы вывести значение studentCount. Подведем итог:
Обратная операции write — readf; она читает из стандартного потока ввода. Буква «f» в имени взята от «formatted», так как то, что читает эта функция, должно быть всегда представлено в определенном формате.
Также в прошлой главе мы узнали, что стандартный поток ввода называется stdin.
В случае чтения один кусочек головоломки все еще отсутствует: куда сохранить данные. Подведем итоги:
Расположение места, куда необходимо сохранить данные, указывается с помощью адреса переменной. Адресом переменной является точное положение в памяти компьютера, где хранится ее значение.
В D символ ‘&’, напечатанный перед именем, является указанием адреса того, что представляет эта переменная. Например, адресом studentCount является &studentCount. Здесь, &studentCount может быть прочитано как «адрес studentCount», и это недостающий кусок, который заменяет знак вопроса выше:
Набор символа & перед именем означает взятие адреса того, что представляет это имя. Это понятие является основой ссылок и указателей, которые мы увидим в следующих главах.
Я отложу объяснение одной особенности использования readf; сейчас, примем правило о том, что первым аргументом readf должна быть строка «%s»:
Заметка: Как я объясняю ниже, в большинстве случаев в строке также должен присутствовать пробел: » %s».
«%s» указывает, что данные должны быть автоматически преобразованы таким способом, который подходит под тип переменной. Например, когда символы ‘4’ и ‘2’ читаются в переменную типа int, они должны быть преобразованы в целочисленное значение 42.
Программа ниже просит пользователя ввести число учеников. Необходимо нажать клавишу Enter после окончания ввода:
void main ( )
<
write ( «Сколько присутствует учеников? » ) ;
/*
* Объявление переменной, которая будет использована для
* хранения информации, которая читается из потока ввода
*/
int studentCount ;
Пропуск пробельных символов
Даже клавиша Enter, которую мы нажимаем после ввода данных, сохраняется как специальный код и помещается в поток stdin. Это помогает программам обнаруживать, была ли информация введена в одной или нескольких строчках.
Хотя это иногда полезно, такие специфические коды в большинстве случаев не важны для программы и должны быть удалены из ввода. Иначе они блокируют ввод и не дают читать другие данные.
Для демонстрации проблемы, давайте читать также число преподавателей из потока ввода:
К сожалению, теперь программа застряла при чтении второго int:
Хотя пользователь ввел число преподавателей равным 20, специальные коды, которые представляют клавишу Enter при чтении предыдущего значения 100, все еще находятся в потоке ввода и блокируют его. Символы, которые хранятся в потоке ввода аналогичны следующим:
Первый символ [EnterCode] блокирует ввод.
Решением этой проблемы является добавление пробела перед %s для обозначения того, что код клавиши Enter, который может быть встречен перед чтением числа преподавателей, не важен: » %s». Пробелы в строке форматирования используются для чтения и игнорирования нуля или более невидимых символов, которые могут предшествовать вводу. Такие символы включают в себя: сам символ пробела, коды, которые представляют клавишу Enter, символ клавиши Tab и другие, называемые пробельными символами.
В общем случае можно использовать » %s» для любых данных, которые читаются из потока ввода. Программа выше работает как ожидается со следующими изменениями:
Дополнительная информация
Чтобы закомментировать другие комментарии, используйте /+ и +/:
/*
Комментарий, который занимает
несколько строк
*/
Блок комментария, который включает в себя другие комментарии
+/
Тяжело читать код, в котором столько же пробелов, сколько в этом.