Перенаправление потоков. Перенаправление ввода-вывода в Linux

Встроенные возможности перенаправления в Linux предоставляют вам широкий набор инструментов, используемых упрощения для всех видов задач. Умение управлять различными потоками ввода-вывода в значительно увеличит производительность, как при разработке сложного программного обеспечения, так и при управлении файлами с помощью командной строки.

Потоки ввода-вывода

Ввод и вывод в окружении Linux распределяется между тремя потоками:

  • Стандартный ввод (standard input, stdin, поток номер 0)
  • Стандартный вывод (standard output, stdout, номер 1)
  • Стандартная ошибка, или поток диагностики (standard error, stderr, номер 2)

При взаимодействии пользователя с терминалом стандартный ввод передается через клавиатуру пользователя. Стандартный вывод и стандартная ошибка отображаются на терминале пользователя в виде текста. Все эти три потока называются стандартными потоками.

Стандартный ввод

Стандартный входной поток обычно передаёт данные от пользователя к программе. Программы, которые предполагают стандартный ввод, обычно получают входные данные от устройства (например, клавиатуры). Стандартный ввод прекращается по достижении EOF (end-of-file, конец файла). EOF указывает на то, что больше данных для чтения нет.

Чтобы увидеть, как работает стандартный ввод, запустите программу cat. Название этого инструмента означает «concatenate» (связать или объединить что-либо). Обычно этот инструмент используется для объединения содержимого двух файлов. При запуске без аргументов cat открывает командную строку и принимает содержание стандартного ввода.

Теперь введите несколько цифр:

1
2
3
ctrl-d

Вводя цифру и нажимая enter, вы отправляете стандартный ввод запущенной программе cat, которая принимает эти данные. В свою очередь, программа cat отображает полученный ввод в стандартном выводе.

Пользователь может задать EOF, нажав ctrl-d, после чего программа cat остановится.

Стандартный вывод

Стандартный вывод записывает данные, сгенерированные программой. Если стандартный выходной поток не был перенаправлен, он выведет текст в терминал. Попробуйте запустить следующую команду для примера:

echo Sent to the terminal through standard output

Команда echo без дополнительных опций отображает на экране все аргументы, переданные ей в командной строке.

Теперь запустите echo без аргументов:

Команда вернёт пустую строку.

Стандартная ошибка

Этот стандартный поток записывает ошибки, создаваемые программой, которая вышла из строя. Как и стандартный вывод, этот поток отправляет данные в терминал.

Рассмотрим пример потока ошибок команды ls. Команда ls отображает содержимое каталогов.

Без аргументов эта команда возвращает содержимое текущего каталога. Если указать в качестве аргумента ls имя каталога, команда вернёт его содержимое.

Поскольку каталога % не существует, команда вернёт стандартную ошибку:

ls: cannot access %: No such file or directory

Перенаправление потоков

Linux предоставляет специальные команды для перенаправления каждого потока. Эти команды записывают стандартный вывод в файл. Если вывод перенаправлен в несуществующий файл, команда создаст новый файл с таким именем и сохранит в него перенаправленный вывод.

Команды с одной угловой скобкой переписывают существующий контент целевого файла:

  • > — стандартный вывод
  • < — стандартный ввод
  • 2> — стандартная ошибка

Команды с двойными угловыми скобками не переписывают содержимое целевого файла:

  • >> — стандартный вывод
  • << — стандартный ввод
  • 2>> — стандартная ошибка

Рассмотрим следующий пример:

cat > write_to_me.txt
a
b
c
ctrl-d

В данном примере команда cat используется для записи выходных данных в файл.

Просмотрите содержимое write_to_me.txt:

cat write_to_me.txt

Команда должна вернуть:

Снова перенаправьте cat в файл write_to_me.txt и введите три цифры.

cat > write_to_me.txt
1
2
3
ctrl-d

Теперь проверьте содержимое файла.

cat write_to_me.txt

Команда должна вернуть:

Как видите, файл содержит только последние выходные данные, поскольку в команде, перенаправляющей выход, использовалась одна угловая скобка.

Теперь попробуйте запустить ту же команду с двумя угловыми скобками:

cat >> write_to_me.txt
a
b
c
ctrl-d

Откройте write_to_me.txt:

1
2
3
a
b
c

Команды с двойными угловыми скобками не перезаписывают существующий контент, а дополняют его.

Конвейеры

Конвейеры (pipes) перенаправляют потоки вывода одной команды на вход другой. При этом данные, передаваемые второй программе, не отображаются в терминале. На экране данные появятся только после обработки второй программой.

Конвейеры в Linux представлены вертикальной чертой.

Например:

Такая команда передаст вывод ls (содержимое текущего каталога) программе less, которая отображает передаваемые ей данные построчно. Как правило, ls выводит содержимое каталогов подряд, не разбивая на строки. Если перенаправить вывод ls в less, то последняя команда разделит вывод на строки.

Как видите, конвейер может перенаправить вывод одной команды на вход другой, в отличие от > и >>, которые перенаправляют данные только в файлы.

Фильтры

Фильтры – это команды, которые могут изменить перенаправление и вывод конвейера.

Примечание : Фильтры также являются стандартными командами Linux, которые можно использовать и без конвейера.

  • find – выполняет поиск файла по имени.
  • grep – выполняет поиск текста по заданному шаблону.
  • tee – перенаправляет стандартный ввод в стандартный вывод и один или несколько файлов.
  • tr – поиск и замена строк.
  • wc – подсчёт символов, строк и слов.

Примеры перенаправления ввода-вывода

Теперь, когда вы ознакомились с основными понятиями и механизмами перенаправления, рассмотрим несколько базовых примеров их использования.

команда > файл

Такой шаблон перенаправляет стандартный вывод команды в файл.

ls ~ > root_dir_contents.txt

Эта команда передает содержимое root каталога системы в качестве стандартного вывода и записывает вывод в файл root_dir_contents. Это удалит все предыдущее содержимое в файле, так как в команде использована одна угловая скобка.

команда > /dev/null

/dev/null – это специальный файл (так называемое «пустое устройство»), который используется для подавления стандартного потока вывода или диагностики, чтобы избежать нежелательного вывода в консоль. Все данные, попадающие в /dev/null, сбрасываются. Перенаправление в /dev/null обычно используется в сценариях оболочки.

ls > /dev/null

Такая команда сбрасывает стандартный выходной поток, возвращаемый командой ls, передав его в /dev/null.

команда 2 > файл

Этот шаблон перенаправляет стандартный поток ошибок команды в файл, перезаписывая его текущее содержимое.

mkdir "" 2> mkdir_log.txt

Эта команда перенаправит ошибку, вызванную неверным именем каталога, и запишет её в log.txt. Обратите внимание: ошибка по-прежнему отображается в терминале.

команда >> файл

Этот шаблон перенаправляет стандартный выход команды в файл, не переписывая текущего содержимого файла.

echo Written to a new file > data.txt
echo Appended to an existing file"s contents >> data.txt

Эта пара команд сначала перенаправляет вводимый пользователем текст в новый файл, а затем вставляет его в существующий файл, не переписывая его содержимого.

команда 2>>файл

Этот шаблон перенаправляет стандартный поток ошибок команды в файл, не перезаписывая существующего содержимого файла. Он подходит для создания логов ошибок программы или сервиса, поскольку содержимое лога не будет постоянно обновляться.

find "" 2> stderr_log.txt
wc "" 2>> stderr_log.txt

Приведенная выше команда перенаправляет сообщение об ошибке, вызванное неверным аргументом find, в файл stderr_log.txt, а затем добавляет в него сообщение об ошибке, вызванной недействительным аргументом wc.

команда | команда

Этот шаблон перенаправляет стандартный выход первой команды на стандартный вход второй команды.

find /var lib | grep deb

Эта команда ищет в каталоге /var и его подкаталогах имена файлов и расширения deb и возвращает пути к файлам, выделяя шаблон поиска красным цветом.

команда | tee файл

Такой шаблон перенаправляет стандартный вывод команды в файл и переписывает его содержимое, а затем отображает перенаправленный выход в терминале. Если указанного файла не существует, он создает новый файл.

В данном шаблоне команда tee, как правило, используется для просмотра вывода программы и одновременного сохранения его в файл.

wc /etc/magic | tee magic_count.txt

Такая команда передаёт количество символов, строк и слов в файле magic (Linux использует его для определения типов файлов) команде tee, которая отправляет эти данные в терминал и в файл magic_count.txt.

команда | команда | команда >> файл

Этот шаблон перенаправляет стандартный вывод первой команды и фильтрует ее через следующие две команды, а затем добавляет окончательный результат в файл.

ls ~ | grep *tar | tr e E >> ls_log.txt

Такая команда отправляет вывод ls для каталога root команде grep. В свою очередь, grep ищет в полученных данных файлы tar. После этого результат grep передаётся команде tr, которая заменит все символы е символом Е. Полученный результат будет добавлен в файл ls_log.txt (если такого файла не существует, команда создаст его автоматически).

Заключение

Функции перенаправления ввода-вывода в Linux сначала кажутся слишком сложными. Однако работа с перенаправлением – один из важнейших навыков системного администратора.

Чтобы узнать больше о какой-либо команде, используйте:

man command | less

Например:

Такая команда вернёт полный список команд для tee.

Tags:

Нашел в просторах интернета одну полезную команду, чтобы записывать все, что происходит в терминале в файл. Вы составляете полный лог всех команд, которые вы вводите в терминале и то, что выводится на экран.


Чтобы начать запись нужно выполнить команду script . Если вызвать команду script без параметров, то запись будет вестись в файл с именем typescript.


$ script
Скрипт запущен, файл - typescript

Файл будет создан именно в той директории, в которой вы находитесь. Вывести название текущей директории можно командой pwd. Если файл уже существует, то он будет очищен.


В качестве параметра команде script можно указать имя файла, в который вы хотите записывать всю активность в терминале. Пример вызова:


script terminal.log

После ввода команды будет создан файл, но данные в него будут записаны только после того, как вы введете команду exit или нажмете сочетание клавиш Ctrl+D. Пример:


$ script
Скрипт запущен, файл - typescript
$ pwd
/home/user
$ exit
exit
Скрипт выполнен, файл - typescript
$

Мы выполнили команду script. Автоматически был создан файл typescript (если он уже существовал, то он будет очищен). С этого момента система начинает запоминать весь ввод и вывод в терминале. В конце мы вводим команду exit и в файл с именем typescript записывается все, что происходило в терминале с момента ввода команды script.


Если вы хотите, чтобы запись велась уже в существующий файл, тогда нужно добавить опцию -a. В этом случае файл не будет очищен, а новые данные будут добавлены к текущему содержимому. Пример:


$ script my.log <--- Первый вызов: создается пустой файл my.log

$ ifconfig
eth0 Link encap:Ethernet HWaddr bb:aa:cc:dd:aa:bb
..
$ exit <--- Данные записываются в файл my.log
exit

$ script my.log -a <--- Второй вызов с опцией -a. Файл не очищается.
Скрипт запущен, файл - my.log
$ pwd
/home/user
$ exit
exit

Скрипт выполнен, файл - my.log <--- Данные добавляются к содержимому my.log

Еще стоит сказать о полезной опции -c или —command . Данная опция позволяет указать команду, которую нужно выполнить, а вывод этой команды записать файл. Пример:


$ script my.log -c ls
Скрипт запущен, файл - my.log
my.logtemp Ubuntu OneИзображения
ЗагрузкиРабочий стол
Скрипт выполнен, файл - my.log

В данном примере мы вызываем команду script с опцией -c и указываем в качестве параметра опции команду ls , которую нужно выполнить, а вывод команды нужно записать в файл my.log. Команда ls выводит список файлов в текущей директории. Таким образом в файл my.log попадет список файлов.


Получить справку по команде script (по всем ее опциям) можно, выполнив в терминале:

Системное администрирование Linux-систем, да и вообще администрирование любой UNIX-подобной системы. Неразрывно связано с работой в командных оболочках. Для опытных системных администраторов такой способ взаимодействия с системой является гораздо более удобным и быстрым, нежели использование графических оболочек и интерфейсов. Конечно, это многим сразу покажется преувеличением, но это неоспоримый факт. Постоянно подтверждаемый на практике. А возможна такая ситуация благодаря некоторым хитростям и инструментам, используемых при работе с командной консолью. И одним из таких инструментов является использование информационных каналов и перенаправления потоков данных для процессов.

Немного теории

Следует начать с того, что для каждого процесса система предоставляет в «пользование» как минимум три информационных канала:

  • STDIN – стандартный ввод;
  • STDOUT – стандартный вывод;
  • STDERR – стандартная ошибка.

Строгой принадлежности этих каналов для каждого процесса нет. Сами процессы изначально появившись в системе «не знают» о том, кому и как принадлежат каналы. Они (каналы) являются общесистемным ресурсом. Хотя и устанавливаются ядром от имени процесса. Каналы могут связывать файлы, каналы, связывающие другие процессы, подключения к сети и т. д.

В UNIX-подобных системах, согласно модели ввода-вывода. Каждому из информационных каналов присваивается целочисленное значение в качестве номера. Однако для безопасного доступа. к вышеприведённым каналам. За ними зарезервированы постоянные номера - 0, 1 и 2 для STDIN, STDOUT и STDERR соответственно. Во время выполнения процессов считывание входных данных происходит по STDIN. Через клавиатуру, с вывода другого процесса, файла и т. д.. А выходные данные (через STDOUT), а также данные об ошибках (через STDERR). Выводятся на экран, в файл, на вход (уже через STDIN другого процесса) в другую программу.

Для изменения направления информационных каналов и связи их с файлами существуют специальные инструкции в виде символов <, > и >>. Так, например инструкцией < можно заставить направить процессу по STDIN содержимое файла. А с помощью инструкции > процесс по STDOUT передаёт выходные данные в файл, при этом заменяется всё содержимое существующего файла целиком. При отсутствии он будет создан. Инструкция >> выполняет то же самое, что и >, но не перезаписывает файл, а дописывает вывод в его конец. Для объединения потоков (т. е. для направления их в один и тот же приёмник) STDOUT и STDERR нужно использовать конструкцию >&. Для направления одного из потоков, например STDERR в отдельное место существует инструкция 2>.

Для связывания между собой двух каналов. Например когда нужно вывод одной команды направить на вход другой. Следует использовать для этого инструкцию (символ) «|». Который означает логическую операцию «или». Однако в контексте связывания потоков данная интерпретация не имеет значения, что нередко сбивает с толку новичков. Благодаря таким возможностям можно составлять целые командные конвейеры. Что делает работу в командных оболочках очень эффективной.

Вывод в файл

Направление вывода команды и запись этого вывода в файл /tmp/somemessage:

$ echo “This is a test message” > /tmp/somemessage

В данном случае в качестве команды с выхода которой (по STDOUT) перенаправляются данные в виде текста «This is a test massage » является утилита echo. В результате создасться файл /tmp/somemessage, в котором будет запись «This is a test message». Если файл не был создан то он создасться, если создан, то все данные в нем перезапишутся. Если нужно дописать вывод в конец файла, нужно использовать оператор «>>»

$ echo “This is a test message” >> /tmp/somemessage $ cat /tmp/somemessage This is a test message This is a test message

Как видим из примера вторая команда дописала строчку в конец файла.

Получение данных из файла

Следующий пример демонстрирует перенаправление входа:

$ mail -s "Mail test" john < /tmp/somemessage

В правой части команды (после символа <) находится файл-источник /tmp/somemesage, содержимое которого перенаправляется в утилиту mail (через STDIN), которая в свою очередь, имеет в качестве собственных параметров заголовок письма и адресата (john).

Другие примеры

Следующий пример показывает, почему иногда очень полезно разделять потоки из каналов STDIN и STDERR:

$ find / -name core 2> /dev/null

Дело в том, что команда find / -name core будет «сыпать» сообщениями об ошибках, направляя их по-умолчанию в то же место, что и результаты поиска. Т. е. в терминал командной консоли, что существенно затруднит восприятие информации пользователем. Т. к. искомые результаты затеряются среди многочисленных сообщений об ошибках, связанных с режимом доступа. Конструкция 2>/dev/null заставляет утилиту find отправлять сообщения об ошибках (следующие по каналу STDERR с зарезервированным номером 2) на фиктивное устройство /dev/null, оставляя в выводе только результаты поиска.

$ find / -name core > /tmp/corefiles 2> /dev/null

Здесь конструкция > /tmp/corefiles перенаправляет вывод утилиты find (по каналу STDOUT) в файл /tmp/corefiles. Сообщения об ошибках отсеиваются на /dev/null, не попадая в вывод терминала командной консоли.

Для связывания между собой разных каналов для разных команд:

$ fsck --help | drep M

M не проверять примонтированные файловые системы

Эта команда выведет строку (или строки), содержащие символ «M» из страницы быстрой справки к утилите . Это очень удобно, когда нужно посмотреть только интересующую информацию. В данном случае утилита grep получает вывод (по инструкции |) от команды fsck —help. И далее по шаблону «M» отбрасывает всё лишнее.

Если нужно, чтобы следующая в конвейере команда выполнялась только после полного и успешного завершения предыдущей команды, то для этого следует использовать инструкцию &&, например:

$ lpr /tmp/t2 && rm /tmp/page1

Эта команда удалит файл /tmp/page1 только тогда, когда содержимое из него будет отправлено из очереди на печать. Для достижения обратного эффекта, т. е. когда нужно выполнение следующей команды в конвейере только после того, как предыдущая не выполнится (завершится с ошибкой с ненулевым кодом), то следует использовать конструкцию ||.

Когда строка кода, включающая слишком длинный конвейер команд тяжело воспринимается, можно разбивать её на логические компоненты по строкам с помощью символа обратной черты «\»:

$ ср --preserve --recursive /etc/* /spare/backup \ || echo "Make backup error"

Отдельные команды, которые должны выполняться друг за другом можно объединять в одну строку, разделяя их символом двоеточия «;»:

Если вы нашли ошибку, пожалуйста, выделите фрагмент текста и нажмите Ctrl+Enter .

Если вы уже освоились с основами терминала, возможно, вы уже готовы к тому, чтобы комбинировать изученные команды. Иногда выполнения команд оболочки по одной вполне достаточно для решения некоей задачи, но в некоторых случаях вводить команду за командой слишком утомительно и нерационально. В подобной ситуации нам пригодятся некоторые особые символы, вроде угловых скобок.

Для оболочки, интерпретатора команд Linux, эти дополнительные символы - не пустая трата места на экране. Они - мощные команды, которые могут связывать воедино различные фрагменты информации, разделять то, что было до этого цельным, и делать ещё много всего. Одна из самых простых, и, в то же время, мощных и широко используемых возможностей оболочки - это перенаправление стандартных потоков ввода/вывода.

Три стандартных потока ввода/вывода

Для того, чтобы понять то, о чём мы будем тут говорить, важно знать, откуда берутся данные, которые можно перенаправлять, и куда они идут. В Linux существует три стандартных потока ввода/вывода данных.

Первый - это стандартный поток ввода (standard input). В системе это - поток №0 (так как в компьютерах счёт обычно начинается с нуля). Номера потоков ещё называют дескрипторами. Этот поток представляет собой некую информацию, передаваемую в терминал, в частности - инструкции, переданные в оболочку для выполнения. Обычно данные в этот поток попадают в ходе ввода их пользователем с клавиатуры.

Второй поток - это стандартный поток вывода (standard output), ему присвоен номер 1. Это поток данных, которые оболочка выводит после выполнения каких-то действий. Обычно эти данные попадают в то же окно терминала, где была введена команда, вызвавшая их появление.

И, наконец, третий поток - это стандартный поток ошибок (standard error), он имеет дескриптор 2. Этот поток похож на стандартный поток вывода, так как обычно то, что в него попадает, оказывается на экране терминала. Однако, он, по своей сути, отличается от стандартного вывода, как результат, этими потоками, при желании, можно управлять раздельно. Это полезно, например, в следующей ситуации. Есть команда, которая обрабатывает большой объём данных, выполняя сложную и подверженную ошибкам операцию. Нужно, чтобы полезные данные, которые генерирует эта команда, не смешивались с сообщениями об ошибках. Реализуется это благодаря раздельному перенаправлению потоков вывода и ошибок.

Как вы, вероятно, уже догадались, перенаправление ввода/вывода означает работу с вышеописанными потоками и перенаправление данных туда, куда нужно программисту. Делается это с использованием символов > и < в различных комбинациях, применение которых зависит от того, куда, в итоге, должны попасть перенаправляемые данные.

Перенаправление стандартного потока вывода

Предположим, вы хотите создать файл, в который будут записаны текущие дата и время. Дело упрощает то, что имеется команда, удачно названная date , которая возвращает то, что нам нужно. Обычно команды выводят данные в стандартный поток вывода. Для того, чтобы эти данные оказались в файле, нужно добавить символ > после команды, перед именем целевого файла. До и после > надо поставить пробел.

При использовании перенаправления любой файл, указанный после > будет перезаписан. Если в файле нет ничего ценного и его содержимое можно потерять, в нашей конструкции допустимо использовать уже существующий файл. Обычно же лучше использовать в подобном случае имя файла, которого пока не существует. Этот файл будет создан после выполнения команды. Назовём его date.txt . Расширение файла после точки обычно особой роли не играет, но расширения помогают поддерживать порядок. Итак, вот наша команда:

$ date > date.txt
Нельзя сказать, что сама по себе эта команда невероятно полезна, однако, основываясь на ней, мы уже можем сделать что-то более интересное. Скажем, вы хотите узнать, как меняются маршруты вашего трафика, идущего через интернет к некоей конечной точке, ежедневно записывая соответствующие данные. В решении этой задачи поможет команда traceroute , которая сообщает подробности о маршруте трафика между нашим компьютером и конечной точкой, задаваемой при вызове команды в виде URL. Данные включают в себя сведения обо всех маршрутизаторах, через которые проходит трафик.

Так как файл с датой у нас уже есть, будет вполне оправдано просто присоединить к этому файлу данные, полученные от traceroute . Для того, чтобы это сделать, надо использовать два символа > , поставленные один за другим. В результате новая команда, перенаправляющая вывод в файл, но не перезаписывающая его, а добавляющая новые данные после старых, будет выглядеть так:

$ traceroute google.com >> date.txt
Теперь нам осталось лишь изменить имя файла на что-нибудь более осмысленное, используя команду mv , которой, в качестве первого аргумента, передаётся исходное имя файла, а в качестве второго - новое:

$ mv date.txt trace1.txt

Перенаправление стандартного потока ввода

Используя знак < вместо > мы можем перенаправить стандартный ввод, заменив его содержимым файла.

Предположим, имеется два файла: list1.txt и list2.txt , каждый из которых содержит неотсортированный список строк. В каждом из списков имеются уникальные для него элементы, но некоторые из элементов список совпадают. Мы можем найти строки, которые имеются и в первом, и во втором списках, применив команду comm , но прежде чем её использовать, списки надо отсортировать.

Существует команда sort , которая возвращает отсортированный список в терминал, не сохраняя отсортированные данные в файл, из которого они были взяты. Можно отправить отсортированную версию каждого списка в новый файл, используя команду > , а затем воспользоваться командой comm . Однако, такой подход потребует как минимум двух команд, хотя то же самое можно сделать в одной строке, не создавая при этом ненужных файлов.

Итак, мы можем воспользоваться командой < для перенаправления отсортированной версии каждого файла команде comm . Вот что у нас получилось:

$ comm <(sort list1.txt) <(sort list2.txt)
Круглые скобки тут имеют тот же смысл, что и в математике. Оболочка сначала обрабатывает команды в скобках, а затем всё остальное. В нашем примере сначала производится сортировка строк из файлов, а потом то, что получилось, передаётся команде comm , которая затем выводит результат сравнения списков.

Перенаправление стандартного потока ошибок

И, наконец, поговорим о перенаправлении стандартного потока ошибок. Это может понадобиться, например, для создания лог-файлов с ошибками или объединения в одном файле сообщений об ошибках и возвращённых некоей командой данных.

Например, что если надо провести поиск во всей системе сведений о беспроводных интерфейсах, которые доступны пользователям, у которых нет прав суперпользователя? Для того, чтобы это сделать, можно воспользоваться мощной командой find .

Обычно, когда обычный пользователь запускает команду find по всей системе, она выводит в терминал и полезные данные и ошибки. При этом, последних обычно больше, чем первых, что усложняет нахождение в выводе команды того, что нужно. Решить эту проблему довольно просто: достаточно перенаправить стандартный поток ошибок в файл, используя команду 2> (напомним, 2 - это дескриптор стандартного потока ошибок). В результате на экран попадёт только то, что команда отправляет в стандартный вывод:

$ find / -name wireless 2> denied.txt
Как быть, если нужно сохранить результаты работы команды в отдельный файл, не смешивая эти данные со сведениями об ошибках? Так как потоки можно перенаправлять независимо друг от друга, в конец нашей конструкции можно добавить команду перенаправления стандартного потока вывода в файл:

$ find / -name wireless 2> denied.txt > found.txt
Обратите внимание на то, что первая угловая скобка идёт с номером - 2> , а вторая без него. Это так из-за того, что стандартный вывод имеет дескриптор 1, и команда > подразумевает перенаправление стандартного вывода, если номер дескриптора не указан.

И, наконец, если нужно, чтобы всё, что выведет команда, попало в один файл, можно перенаправить оба потока в одно и то же место, воспользовавшись командой &> :

$ find / -name wireless &> results.txt

Итоги

Тут мы разобрали лишь основы механизма перенаправления потоков в интерпретаторе командной строки Linux, однако даже то немногое, что вы сегодня узнали, даёт вам практически неограниченные возможности. И, кстати, как и всё остальное, что касается работы в терминале, освоение перенаправления потоков требует практики. Поэтому рекомендуем вам приступить к собственным экспериментам с > и < .

Уважаемые читатели! Знаете ли вы интересные примеры использования перенаправления потоков в Linux, которые помогут новичкам лучше освоиться с этим приёмом работы в терминале?

Вы уже знакомы с двумя методами работы с тем, что выводят сценарии командной строки:

  • Отображение выводимых данных на экране.
  • Перенаправление вывода в файл.
Иногда что-то надо показать на экране, а что-то - записать в файл, поэтому нужно разобраться с тем, как в Linux обрабатывается ввод и вывод, а значит - научиться отправлять результаты работы сценариев туда, куда нужно. Начнём с разговора о стандартных дескрипторах файлов.

Стандартные дескрипторы файлов

Всё в Linux - это файлы, в том числе - ввод и вывод. Операционная система идентифицирует файлы с использованием дескрипторов.

Каждому процессу позволено иметь до девяти открытых дескрипторов файлов. Оболочка bash резервирует первые три дескриптора с идентификаторами 0, 1 и 2. Вот что они означают.

  • 0 , STDIN - стандартный поток ввода.
  • 1 , STDOUT - стандартный поток вывода.
  • 2 , STDERR - стандартный поток ошибок.
Эти три специальных дескриптора обрабатывают ввод и вывод данных в сценарии.
Вам нужно как следует разобраться в стандартных потоках. Их можно сравнить с фундаментом, на котором строится взаимодействие скриптов с внешним миром. Рассмотрим подробности о них.

STDIN

STDIN - это стандартный поток ввода оболочки. Для терминала стандартный ввод - это клавиатура. Когда в сценариях используют символ перенаправления ввода - < , Linux заменяет дескриптор файла стандартного ввода на тот, который указан в команде. Система читает файл и обрабатывает данные так, будто они введены с клавиатуры.

Многие команды bash принимают ввод из STDIN , если в командной строке не указан файл, из которого надо брать данные. Например, это справедливо для команды cat .

Когда вы вводите команду cat в командной строке, не задавая параметров, она принимает ввод из STDIN . После того, как вы вводите очередную строку, cat просто выводит её на экран.

STDOUT

STDOUT - стандартный поток вывода оболочки. По умолчанию это - экран. Большинство bash-команд выводят данные в STDOUT , что приводит к их появлению в консоли. Данные можно перенаправить в файл, присоединяя их к его содержимому, для этого служит команда >> .

Итак, у нас есть некий файл с данными, к которому мы можем добавить другие данные с помощью этой команды:

Pwd >> myfile
То, что выведет pwd , будет добавлено к файлу myfile , при этом уже имеющиеся в нём данные никуда не денутся.

Перенаправление вывода команды в файл

Пока всё хорошо, но что если попытаться выполнить что-то вроде показанного ниже, обратившись к несуществующему файлу xfile , задумывая всё это для того, чтобы в файл myfile попало сообщение об ошибке.

Ls –l xfile > myfile
После выполнения этой команды мы увидим сообщения об ошибках на экране.


Попытка обращения к несуществующему файлу

При попытке обращения к несуществующему файлу генерируется ошибка, но оболочка не перенаправила сообщения об ошибках в файл, выведя их на экран. Но мы-то хотели, чтобы сообщения об ошибках попали в файл. Что делать? Ответ прост - воспользоваться третьим стандартным дескриптором.

STDERR

STDERR представляет собой стандартный поток ошибок оболочки. По умолчанию этот дескриптор указывает на то же самое, на что указывает STDOUT , именно поэтому при возникновении ошибки мы видим сообщение на экране.

Итак, предположим, что надо перенаправить сообщения об ошибках, скажем, в лог-файл, или куда-нибудь ещё, вместо того, чтобы выводить их на экран.

▍Перенаправление потока ошибок

Как вы уже знаете, дескриптор файла STDERR - 2. Мы можем перенаправить ошибки, разместив этот дескриптор перед командой перенаправления:

Ls -l xfile 2>myfile cat ./myfile
Сообщение об ошибке теперь попадёт в файл myfile .


Перенаправление сообщения об ошибке в файл

▍Перенаправление потоков ошибок и вывода

При написании сценариев командной строки может возникнуть ситуация, когда нужно организовать и перенаправление сообщений об ошибках, и перенаправление стандартного вывода. Для того, чтобы этого добиться, нужно использовать команды перенаправления для соответствующих дескрипторов с указанием файлов, куда должны попадать ошибки и стандартный вывод:

Ls –l myfile xfile anotherfile 2> errorcontent 1> correctcontent

Перенаправление ошибок и стандартного вывода

Оболочка перенаправит то, что команда ls обычно отправляет в STDOUT , в файл correctcontent благодаря конструкции 1> . Сообщения об ошибках, которые попали бы в STDERR , оказываются в файле errorcontent из-за команды перенаправления 2> .

Если надо, и STDERR , и STDOUT можно перенаправить в один и тот же файл, воспользовавшись командой &> :


Перенаправление STDERR и STDOUT в один и тот же файл

После выполнения команды то, что предназначено для STDERR и STDOUT , оказывается в файле content .

Перенаправление вывода в скриптах

Существует два метода перенаправления вывода в сценариях командной строки:
  • Временное перенаправление, или перенаправление вывода одной строки.
  • Постоянное перенаправление, или перенаправление всего вывода в скрипте либо в какой-то его части.

▍Временное перенаправление вывода

В скрипте можно перенаправить вывод отдельной строки в STDERR . Для того, чтобы это сделать, достаточно использовать команду перенаправления, указав дескриптор STDERR , при этом перед номером дескриптора надо поставить символ амперсанда (&):

#!/bin/bash echo "This is an error" >&2 echo "This is normal output"
Если запустить скрипт, обе строки попадут на экран, так как, как вы уже знаете, по умолчанию ошибки выводятся туда же, куда и обычные данные.


Временное перенаправление

Запустим скрипт так, чтобы вывод STDERR попадал в файл.

./myscript 2> myfile
Как видно, теперь обычный вывод делается в консоль, а сообщения об ошибках попадают в файл.


Сообщения об ошибках записываются в файл

▍Постоянное перенаправление вывода

Если в скрипте нужно перенаправлять много выводимых на экран данных, добавлять соответствующую команду к каждому вызову echo неудобно. Вместо этого можно задать перенаправление вывода в определённый дескриптор на время выполнения скрипта, воспользовавшись командой exec:

#!/bin/bash exec 1>outfile echo "This is a test of redirecting all output" echo "from a shell script to another file." echo "without having to redirect every line"
Запустим скрипт.


Перенаправление всего вывода в файл

Если просмотреть файл, указанный в команде перенаправления вывода, окажется, что всё, что выводилось командами echo , попало в этот файл.

Команду exec можно использовать не только в начале скрипта, но и в других местах:

#!/bin/bash exec 2>myerror echo "This is the start of the script" echo "now redirecting all output to another location" exec 1>myfile echo "This should go to the myfile file" echo "and this should go to the myerror file" >&2
Вот что получится после запуска скрипта и просмотра файлов, в которые мы перенаправляли вывод.


Перенаправление вывода в разные файлы

Сначала команда exec задаёт перенаправление вывода из STDERR в файл myerror . Затем вывод нескольких команд echo отправляется в STDOUT и выводится на экран. После этого команда exec задаёт отправку того, что попадает в STDOUT , в файл myfile , и, наконец, мы пользуемся командой перенаправления в STDERR в команде echo , что приводит к записи соответствующей строки в файл myerror.

Освоив это, вы сможете перенаправлять вывод туда, куда нужно. Теперь поговорим о перенаправлении ввода.

Перенаправление ввода в скриптах

Для перенаправления ввода можно воспользоваться той же методикой, которую мы применяли для перенаправления вывода. Например, команда exec позволяет сделать источником данных для STDIN какой-нибудь файл:

Exec 0< myfile
Эта команда указывает оболочке на то, что источником вводимых данных должен стать файл myfile , а не обычный STDIN . Посмотрим на перенаправление ввода в действии:

#!/bin/bash exec 0< testfile count=1 while read line do echo "Line #$count: $line" count=$(($count + 1)) done
Вот что появится на экране после запуска скрипта.


Перенаправление ввода

В одном из предыдущих материалов вы узнали о том, как использовать команду read для чтения данных, вводимых пользователем с клавиатуры. Если перенаправить ввод, сделав источником данных файл, то команда read , при попытке прочитать данные из STDIN , будет читать их из файла, а не с клавиатуры.

Некоторые администраторы Linux используют этот подход для чтения и последующей обработки лог-файлов.

Создание собственного перенаправления вывода

Перенаправляя ввод и вывод в сценариях, вы не ограничены тремя стандартными дескрипторами файлов. Как уже говорилось, можно иметь до девяти открытых дескрипторов. Остальные шесть, с номерами от 3 до 8, можно использовать для перенаправления ввода или вывода. Любой из них можно назначить файлу и использовать в коде скрипта.

Назначить дескриптор для вывода данных можно, используя команду exec:

#!/bin/bash exec 3>myfile echo "This should display on the screen" echo "and this should be stored in the file" >&3 echo "And this should be back on the screen"
После запуска скрипта часть вывода попадёт на экран, часть - в файл с дескриптором 3 .


Перенаправление вывода, используя собственный дескриптор

Создание дескрипторов файлов для ввода данных

Перенаправить ввод в скрипте можно точно так же, как и вывод. Сохраните STDIN в другом дескрипторе, прежде чем перенаправлять ввод данных.

После окончания чтения файла можно восстановить STDIN и пользоваться им как обычно:

#!/bin/bash exec 6<&0 exec 0< myfile count=1 while read line do echo "Line #$count: $line" count=$(($count + 1)) done exec 0<&6 read -p "Are you done now? " answer case $answer in y) echo "Goodbye";; n) echo "Sorry, this is the end.";; esac
Испытаем сценарий.


Перенаправление ввода

В этом примере дескриптор файла 6 использовался для хранения ссылки на STDIN . Затем было сделано перенаправление ввода, источником данных для STDIN стал файл. После этого входные данные для команды read поступали из перенаправленного STDIN , то есть из файла.

После чтения файла мы возвращаем STDIN в исходное состояние, перенаправляя его в дескриптор 6 . Теперь, для того, чтобы проверить, что всё работает правильно, скрипт задаёт пользователю вопрос, ожидает ввода с клавиатуры и обрабатывает то, что введено.

Закрытие дескрипторов файлов

Оболочка автоматически закрывает дескрипторы файлов после завершения работы скрипта. Однако, в некоторых случаях нужно закрывать дескрипторы вручную, до того, как скрипт закончит работу. Для того, чтобы закрыть дескриптор, его нужно перенаправить в &- . Выглядит это так:

#!/bin/bash exec 3> myfile echo "This is a test line of data" >&3 exec 3>&- echo "This won"t work" >&3
После исполнения скрипта мы получим сообщение об ошибке.


Попытка обращения к закрытому дескриптору файла

Всё дело в том, что мы попытались обратиться к несуществующему дескриптору.

Будьте внимательны, закрывая дескрипторы файлов в сценариях. Если вы отправляли данные в файл, потом закрыли дескриптор, потом - открыли снова, оболочка заменит существующий файл новым. То есть всё то, что было записано в этот файл ранее, будет утеряно.

Получение сведений об открытых дескрипторах

Для того, чтобы получить список всех открытых в Linux дескрипторов, можно воспользоваться командой lsof . Во многих дистрибутивах, вроде Fedora, утилита lsof находится в /usr/sbin . Эта команда весьма полезна, так как она выводит сведения о каждом дескрипторе, открытом в системе. Сюда входит и то, что открыли процессы, выполняемые в фоне, и то, что открыто пользователями, вошедшими в систему.

У этой команды есть множество ключей, рассмотрим самые важные.

  • -p Позволяет указать ID процесса.
  • -d Позволяет указать номер дескриптора, о котором надо получить сведения.
Для того, чтобы узнать PID текущего процесса, можно использовать специальную переменную окружения $$ , в которую оболочка записывает текущий PID .

Ключ -a используется для выполнения операции логического И над результатами, возвращёнными благодаря использованию двух других ключей:

Lsof -a -p $$ -d 0,1,2

Вывод сведений об открытых дескрипторах

Тип файлов, связанных с STDIN , STDOUT и STDERR - CHR (character mode, символьный режим). Так как все они указывают на терминал, имя файла соответствует имени устройства, назначенного терминалу. Все три стандартных файла доступны и для чтения, и для записи.

Посмотрим на вызов команды lsof из скрипта, в котором открыты, в дополнение к стандартным, другие дескрипторы:

#!/bin/bash exec 3> myfile1 exec 6> myfile2 exec 7< myfile3 lsof -a -p $$ -d 0,1,2,3,6,7
Вот что получится, если этот скрипт запустить.


Просмотр дескрипторов файлов, открытых скриптом

Скрипт открыл два дескриптора для вывода (3 и 6) и один - для ввода (7). Тут же показаны и пути к файлам, использованных для настройки дескрипторов.

Подавление вывода

Иногда надо сделать так, чтобы команды в скрипте, который, например, может исполняться как фоновый процесс, ничего не выводили на экран. Для этого можно перенаправить вывод в /dev/null . Это - что-то вроде «чёрной дыры».

Вот, например, как подавить вывод сообщений об ошибках:

Ls -al badfile anotherfile 2> /dev/null
Тот же подход используется, если, например, надо очистить файл, не удаляя его:

Cat /dev/null > myfile

Итоги

Сегодня вы узнали о том, как в сценариях командной строки работают ввод и вывод. Теперь вы умеете обращаться с дескрипторами файлов, создавать, просматривать и закрывать их, знаете о перенаправлении потоков ввода, вывода и ошибок. Всё это очень важно в деле разработки bash-скриптов.

В следующий раз поговорим о сигналах Linux, о том, как обрабатывать их в сценариях, о запуске заданий по расписанию и о фоновых задачах.

Уважаемые читатели! В этом материале даны основы работы с потоками ввода, вывода и ошибок. Уверены, среди вас есть профессионалы, которые могут рассказать обо всём этом то, что приходит лишь с опытом. Если так - передаём слово вам.



В продолжение темы:
Android

Популярная социальная сеть ВКонтакте позволяет находить новых друзей и держать контакт со всеми близкими. Помимо этого, каждый пользователь может делиться собственными...