Для того, чтобы записать данные в файл или прочитать их оттуда, процессу необходимо сначала открыть этот файл (при открытии на запись, возможно, придётся предварительно создать его). При этом процесс получает дескриптор (описатель) открытого файла — уникальное для этого процесса число, которое он и будет использовать во всех операциях записи. Первый открытый файл получит дескриптор 0
, второй — 1
и так далее. Закончив работу с файлом, процесс закрывает его, при этом дескриптор освобождается и может быть использован повторно. Если процесс завершается, не закрыв файлы, за него это делает система. Строго говоря, только в операции открытия дескриптора указывается, какой именно файл будет использоваться. В качестве «файла» используются и обычные файлы, и файлы-дырки (чаще всего — терминалы), и каналы, описанные в разделе Pipeline..Конвейер. Дальнейшие операции — чтение, запись и закрытие, работают с дескриптором, как с потоком данных, а куда именно ведёт этот поток, неважно.
Каждый процесс Linux получает при старте три «файла», открытых для него системой. Первый из них (дескриптор 0
) открыт на чтение, это стандартный ввод процесса. Именно со стандартным вводом работают все операции чтения, если в них не указан дескриптор файла. Второй (дескриптор 1
) — открыт на запись
, это стандартный вывод процесса. С ним работают все операции записи, если дескриптор файла не указан в них явно. Наконец, третий поток данных (дескриптор 2
) предназначается для вывода диагностических сообщений, он называется стандартный вывод ошибок. Поскольку эти три дескриптора уже открыты к моменту запуска процесса, первый файл, открытый самим процессом, будет, скорее всего, иметь дескриптор 3
.
- дескриптор
- Описатель потока данных, открытого процессом. Дескрипторы нумеруются начиная с
0
. При открытии нового потока данных его дескриптор получает наименьший из неиспользуемых в этот момент номеров. Три заранее открытых дескриптора: стандартный ввод (0
), стандартный вывод (1
) и стандартный вывод ошибок (2
) процессу выдаются при запуске.
Механизм копирования окружения, описанный в лекции Доступ процессов к файлам и каталогам, подразумевает, в числе прочего, копирование всех открытых дескрипторов родительского процесса дочернему. В результате, и родительский, и дочерний процесс имеют под одинаковыми дескрипторами одни и те же потоки данных. Когда запускается стартовый командный интерпретатор, все три заранее открытых дескриптора связаны у него с терминалом (точнее, с соответствующим файлом-дыркой типа tty
): пользователь вводит команды с клавиатуры и видит сообщения на экране. Следовательно, любая команда, запускаемая из командной оболочки, будет выводить на тот же терминал, а любая команда, запущенная интерактивно (не в фоне) — вводить оттуда.
Стандартный вывод
Мефодий уже сталкивался с тем, что некоторые программы умеют выводить не только на терминал, но и в файл, например, info
при указании параметрического ключа «-o
» с именем файла выведет текст руководства в файл, вместо того, чтобы отображать его на мониторе. Даже если разработчиками программы не предусмотрен такой ключ, Мефодию известен и другой способ сохранить вывод программы в файле вместо того, чтобы выводить его на монитор: поставить знак «>
» и указать после него имя файла. Таким образом Мефодий уже создавал короткие текстовые файлы (сценарии) при помощи утилиты cat
(см. лекцию Доступ процессов к файлам и каталогам).
[methody@localhost methody]$ cat > textfile
Это файл для примеров.
^D
[methody@localhost methody]$ ls -l textfile
-rw-r--r-- 1 methody methody 23 Ноя 15 16:06 textfile
Пример 2. Перенаправление стандартного вывода в файл
От использования символа «>
» возможности самой утилиты cat
, конечно, не расширились. Более того, cat
в этом примере не получила от командной оболочки никаких параметров: ни знака «>
», ни последующего имени файла. В этом случае cat
работала как обычно, не зная (и даже не интересуясь!), куда попадут выведенные данные: на экран монитора, в файл или куда-нибудь ещё. Вместо того, чтобы самой обеспечивать доставку вывода до конечного адресата (будь то человек или файл), cat
отправляет все данные на стандартный вывод (сокращённо — stdout).
Подмена стандартного вывода — задача командной оболочки (shell). В данном примере shell создаёт пустой файл, имя которого указано после знака «>
», и дескриптор этого файла передаётся программе cat
под номером 1
(стандартный вывод). Делается это очень просто. В лекции Доступ процессов к файлам и каталогам было рассказано о том, как запускаются команды из оболочки. В частности, после выполнения fork()
появляется два одинаковых процесса, один из которых — дочерний — должен запустить вместо себя команду (выполнить exec()
). Так вот, перед этим он закрывает стандартный вывод (дескриптор 1
освобождается) и открывает файл (с ним связывается первый свободный дескриптор, т. е. 1
), а запускаемой команде ничего знать и не надо: её стандартный вывод уже подменён. Эта операция называется перенаправлением стандартного вывода. В том случае, если файл уже существует, shell запишет его заново, полностью уничтожив всё, что в нём содержалось до этого. Поэтому Мефодию, чтобы продолжить записывать данные в textfile
, потребуется другая операция — «>>
».
[methody@localhost methody]$ cat >> textfile
Пример 1.
^D
[methody@localhost methody]$ cat textfile
Это файл для примеров.
Пример 1.
[methody@localhost methody]$
Пример 3. Недеструктивное перенаправление стандартного вывода
Мефодий получил именно тот результат, который ему требовался: добавил в конец уже существующего файла данные со стандартного вывода очередной команды.
- стандартный вывод
- Поток данных, открываемый системой для каждого процесса в момент его запуска, и предназначенный для данных, выводимых процессом.
Стандартный ввод
Аналогичным образом для передачи данных на вход программе может быть использован стандартный ввод (сокращённо — stdin). При работе с командной строкой стандартный ввод — это символы, вводимые пользователем с клавиатуры. Стандартный ввод можно перенаправить при помощи командной оболочки, подав на него данные из некоторого файла. Символ «<
» служит для перенаправления содержимого файла на стандартный ввод программе. Например, если вызвать утилиту sort
без параметра, она будет читать строки со стандартного ввода. Команда «sort < имя_файла
» подаст на ввод sort
данные из файла.
[methody@localhost methody]$ sort < textfile
Пример 1.
Это файл для примеров.
[methody@localhost methody]$
Пример 4. Перенаправление стандартного ввода из файла
Результат действия этой команды совершенно аналогичен команде sort textfile
, разница в том, что когда используется «<
», sort
получает данные со стандартного ввода, ничего не зная о файле «textfile
», откуда они поступают. Механизм работы shell в данном случае тот же, что и при перенаправлении вывода: shell читает данные из файла «textfile
», запускает утилиту sort
и передаёт ей на стандартный ввод содержимое файла.
Стоит помнить, что операция «>
» деструктивна: она всегда создаёт файл нулевой длины. Поэтому для, допустим, сортировки данных в файле надо применять последовательно sort < файл > новый_файл
и mv новый_файл файл
. Команда вида команда < файл > тот_же_файл
просто урежет его до нулевой длины!
- стандартный ввод
- Поток данных, открываемый системой для каждого процесса в момент его запуска, и предназначенный для ввода данных.
Стандартный вывод ошибок
В качестве первого примера и упражнения на перенаправление Мефодий решил записать руководство по cat
в свой файл cat.info
:
[methody@localhost methody]$ info cat > cat.info
info: Запись ноды (coreutils.info.bz2)cat invocation...
info: Завершено.
[methody@localhost methody]$ head -1 cat.info
File: coreutils.info, Node: cat invocation, Next: tac invocation, Up: Output of entire files
[methody@localhost methody]$
Пример 5. Стандартный вывод ошибок
Удивлённый Мефодий обнаружил, что вопреки его указанию отправляться в файл две строки, выведенные командой info
, всё равно проникли на терминал. Очевидно, эти строки не попали на стандартный вывод потому, что не относятся непосредственно к руководству, которое должна вывести программа, они информируют пользователя о ходе выполнения работы: записи руководства в файл. Для такого рода диагностических сообщений, а также для сообщений об ошибках, возникших в ходе выполнения программы, в Linux предусмотрен стандартный вывод ошибок (сокращённо — stderr).
- стандартный вывод ошибок
- Поток данных, открываемый системой для каждого процесса в момент его запуска, и предназначенный для диагностических сообщений, выводимых процессом.
Использование стандартного вывода ошибок наряду со стандартным выводом позволяет отделить собственно результат работы программы от разнообразной сопровождающей информации, например, направив их в разные файлы. Стандартный вывод ошибок может быть перенаправлен так же, как и стандартный ввод/вывод, для этого используется комбинация символов «2
».
[methody@localhost methody]$ info cat > cat.info 2> cat.stderr
[methody@localhost methody]$ cat cat.stderr
info: Запись ноды (coreutils.info.bz2)cat invocation...
info: Завершено.
[methody@localhost methody]$
Пример 6. Перенаправление стандартного вывода ошибок
В этот раз на терминал уже ничего не попало, стандартный вывод отправился в файл cat.info
, стандартный вывод ошибок — в cat.stderr
. Вместо «>
» и «2
» Мефодий мог бы написать «1>
» и «2>
». Цифры в данном случае обозначают номера дескрипторов открываемых файлов. Если некая утилита ожидает получить открытый дескриптор с номером, допустим, 4
, то чтобы её запустить обязательно потребуется использовать сочетание «4>
».
Иногда, однако, требуется объединить стандартный вывод и страндартный вывод ошибок в одном файле, а не разделять их. В командной оболочке bash
для этого имеется специальная последовательность «2>&1
». Это означает «направить стандартный вывод ошибок туда же, куда и стандартный вывод»:
[methody@localhost methody]$ info cat > cat.info 2>&1
[methody@localhost methody]$ head -3 cat.info
info: Запись ноды (coreutils.info.bz2)cat invocation...
info: Завершено.
File: coreutils.info, Node: cat invocation, Next: tac invocation, Up: Output of entire files
[methody@localhost methody]$
Пример 7. Объединение стандартного вывода и стандартного вывода ошибок
В этом примере важен порядок перенаправлений: в командной строке Мефодий сначала указал, куда перенаправить стандартный вывод («> cat.info
») и только потом велел направить туда же стандартный вывод ошибок. Сделай он наоборот («2>&1 > cat.info
»), результат получился бы неожиданный: в файл попал бы только стандартный вывод, а диагностические сообщения появились бы на терминале. Однако логика здесь железная: на момент выполнения операции «2>&1
» стандартный вывод был связан с терминалом, значит, после её выполнения стандартный вывод ошибок тоже будет связан с терминалом. А последующее перенаправление стандартного вывода в файл, конечно, никак не отразится на стандартном выводе ошибок. Номер в конструкции «&номер
» — это номер открытого дескриптора. Если бы упомянутая выше утилита, записывающая в четвёртый дескриптор, была написана на shell, в ней бы использовались перенаправления вида «>&4
». Чтобы не набирать громоздкую конструкцию «> файл 2>&1
» в bash
используются сокращения: «&> файл
» или, что то же самое, «>& файл
».
Перенаправление в никуда
Иногда заведомо известно, что какие-то данные, выведенные программой, не понадобятся. Например, предупреждения со стандартного вывода ошибок. В этом случае можно перенаправить стандартный вывод ошибок в файл-дырку, специально предназначенный для уничтожения данных — /dev/null
. Всё, что записывается в этот файл, просто будет выброшено и нигде не сохранится.
[methody@localhost methody]$ info cat > cat.info 2> /dev/null
[methody@localhost methody]$
Пример 8. Перенаправление в /dev/null
Точно таким же образом можно избавиться и от стандартного вывода, отправив его в /dev/null
.