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

Любая программа - это "автомат", предназначенный для обработки данных: получая на входе одну информацию, она в результате работы выдает другую. Хотя входящая и/или выходящая информация может быть и нулевой, т. е. попросту отсутствовать. Те данные, которые передаются программе для обработки - это ее ввод, то, что она выдает в результате работы - вывод. Организация ввода и вывода для каждой программы - это задача операционной системы.

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

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

Рассмотрим сначала пару команд, с помощью которых можно организовать ввод/вывод.

Команды вывода на стандартное устройство вывода

Linux предоставляет несколько команд для вывода сообщений в стандартный поток вывода:

  • echo - Вывести строку в стандартный поток вывода.
  • printf - Вывести форматированный текст в стандартный поток вывода.
  • yes - Выводить повторяющийся текст в стандартный поток вывода.
  • seq - Вывести последовательность чисел в стандартный поток вывода
  • clear Очистить экран или окно.

Например, при использовании команды echo если указать управляющий символ \с, то по завершении вывода не будет осуществлен переход в новую строку:

$ echo "Как вас зовут?\c"

Как вас зовут?$

Здесь $ – символ приглашения.

В строке также можно вычислять значения переменных интерпретатора shell и даже других команд. Например, следующая команда сообщает о том, каков начальный каталог текущего пользователя (переменная среды $HOME) и к какому терминалу он подключен (команда tty заключена в обратные кавычки, чтобы интерпретатор поместил в строку результат ее выполнения).

$ echo "Ваш начальный каталог - $HOME, вы подключены к терминалу - `tty` "

Ваш начальный каталог - /home/knoppix, вы подключены к терминалу - /dev/tty1

Так как двойные кавычки в интерпретаторе shell имеют специальное назначение, то для того чтобы в выводимую строку включить двойные кавычки нужно отменить их специальное назначение с помощью обратной косой черты (\). Так отменяется назначение любого специального символа.

Например, чтобы вывести строку “/dev/tty1” необходимо выполнить:

$echo “\”/dev/tty1\””

Команды ввода из стандартного устройства ввода

Команда read читает одну строку из стандартного входного потока и записывает ее содержимое в указанные переменные. При указании нескольких переменных в первую из них записывается первое слово, во вторую – второе и т.д. в последнюю – остаток строки.

Следующий сценарий вызывает отдельную команду read для чтения каждой переменной.


$ cat test
#!/bin/bash
echo “Имя: \с”
read name
echo “Фамилия: \c”
read surname
echo “Имя=” $name “Фамилия=” $surname

Тогда для выполнения этого сценария необходимо файлу test дать право выполнения: chmod 0755 test и запустить его./test. Результат выполнения: Имя: Иван Фамилия: Петров Имя=Иван Фамилия=Петров

СТАНДАРТНЫЕ ПОТОКИ ВВОДА, ВЫВОДА И ОШИБОК

Каждая запущенная из командного интерпретатора программа получает три открытых потока ввода/вывода:

Стандартный ввод (sldin) - стандартный вывод(sldout) - стандартный вывод ошибок (stderr)

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

При этом с каждым процессом (командой, сценарием и т.п.), выполняемым в интерпретаторе shell, связан рад открытых файлов, из которых процесс может читать свои данные: и в которые он может записывать их. Каждый из этих файлов идентифицируется числом, называемым дескриптором файла, но первые три файла являются потоками ввода/вывода по умолчанию:

Файл Дескриптор
Стандартный поток ввода 0
Стандартный поток вывода 1
Стандартный поток ошибок 2

В действительности создается 12 открытых файлов, но файлы с дескрипторами 0, 1 и 2 резервируются для стандартных потоков ввода, вывода и ошибок. Пользователи могут также работать с файлами, имеющими дескрипторы от 3 до 9 (зарезервированы).

Файл стандартного потока ввода (sldin) имеет дескриптор 0. Из этого файла процессы извлекают свои входные данные. По умолчанию входной поток ассоциирован с клавиатурой (устройство /dev/tty), но чаше всего он поступает по каналу от других процессов или из обычного файла.

Файл стандартного потока вывода (stdout) имеет дескриптор 1. В этот файл записываются все выходные данные процесса. По умолчанию данные выводятся на экран терминала (устройство/dev/tty), но их можно также перенаправить в файл или послать по каналу другому процессу.

Файл стандартного потока ошибок (siderr) имеет дескриптор 2. В этот файл записываются сообщения об ошибках, возникающих в ходе выполнения команды. По умолчанию сообщения об ошибках выводятся на экран терминала (устройство /dev/tty), но их также можно перенаправить в файл. Зачем же для регистрации ошибок выделять специальный файл? Дело в том, что это очень удобный способ выделения из результатов работы команды собственно выходных данных, а также хорошая возможность эффективно организовать ведение различного рода журнальных файлов.

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

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

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

% cat myfile - создаст в текущей директории пустой файл myfile.

/dev/null - специальный файл, представляющий собой т. н. «пустое устройство». Запись в него происходит успешно, независимо от объёма «записанной» информации. Чтение из /dev/null эквивалентно считыванию конца файла EOF.

Перенаправление потоков ввода-вывода осуществляется, подобно DOS (Точнее, синтаксис перенаправления потоков ОС DOS восприняла от UNIX) с помощью символов:

> - перенаправление стандартного потока вывода
>> - перенаправление стандартного потока вывода в режиме дозаписи
< - перенаправление стандартного потока ввода
<< - получение данные из стандартного потока ввода до тех пор, пока не встретится разделитель

Однако, в отличие от DOS при создании программного канала между двумя процессами ОС UNIX/Linux запускает оба процесса одновременно и осуществляет передачу информации через системный буфер (без промежуточной записи на жесткий диск). Таким образом, программные каналы в ОС UNIX/Linux являются весьма эффективным способом обмена. В случае переполнения системного буфера (например если ``передающая"" программа выдает информацию в канал быстрее чем ее может обработать ``принимающая"" программа) ОС автоматически приостанавливает тот процесс, который осуществляет запись в канал до освобождения буфера.

Наиболее распространенные операторы переадресации

№п/п Синтаксис Описание
1 команда > файл Направляет стандартный поток вывода в новый файл

2 команда 1> файл Направляет стандартный поток вывода в указанный файл

3 команда >> файл Направляет стандартный поток вывода в указанный файл (режим присоединения)

4 команда > файл 2>&1 Направляет стандартные потоки вывода и ошибок в указанный файл

5 команда 2> файл Направляет стандартный поток ошибок в указанный файл

6 команда 2>> файл Направляет стандартный поток ошибок в указанный файл (режим присоединения)

7 команда >> файл 2>&1 Направляет стандартные потоки вывода и ошибок в указанный файл (режим присоединения)

8 команда < файл1 > файл2 Получает входные данные из первого файла и направляет выходные данные во второй файл

9 команда < файл в качестве стандартного входного потока получает данные из указанного файла

10 команда << разделитель Получает данные из стандартного потока ввода до тех пор, пока не встретится разделитель

11 команда <&m В качестве стандартного входного потока получает дан­ные из файла с дескриптором m

12 команда >&m Направляет стандартный поток вывода в файл с дескриптором m

Оператор n>&m позволяет перенаправить файл с дескриптором n туда, куда направлен файл с дескриптором m. Подобных операторов в командной строке может быть несколько, в этом случае они вычисляются слева направо.

Команда exec и применение дескрипторов файлов

Команда exec заменяет текущий интерпретатор shell указанной командой. Обычно она используется для того, чтобы закрыть текущий интерпретатор и запустить другой. Но у нее есть и другое применение.

Например, команда вида

Exec < файл делает указанный файл стандартным входным потоком всех команд. Выполнять ее в
интерактивном режиме нет смысла - она предназначена для использования в сценариях,
чтобы все идущие после нее команды читали свои входные данные из файла. В этом случае
в конце сценария обязательно должна стоять команда

Exec <&– которая закрывает стандартный входной поток (в данном случае файл). Подобный прием применяется
преимущественно в сценариях, выполняющихся при выходе из системы.

Команда exec указатель на файл с дескриптором 0 (stdin). Восстановить этот указатель можно будет только по завершении работы сценария.
Если же в сценарии предполагается продолжить чтение данных с клавиатуры, то необходимо сохранить
указатель на прежний входной поток. Ниже приведен небольшой сценарий, в котором демонстрируется, как это сделать.

$ cat f_desc
#!/bin/bash
exec 3<&0 0<file
read linel
read line2
exec 0<&3
echo $1inel
echo $line2

Первая команда exec сохраняет указатель на стандартный входной поток (stdin) в файле с дескриптором 3
(допускается любое целое число в диапазоне от 3 до 9), а затем открывает файл file для чтения. Следующие две команды read
читают из файла две строки текста. Вторая команда exec восстанавливает указатель на стандартный входной поток: теперь
он связан с файлом stdin, а не file. Завершающие команды echo отображают на экране содержимое прочитанных строк,
которые были сохранены в переменных linel и Iine2.

Результат работы сценария:
$ ./ f_desc
Привет!
Пока!

Изучаем Linux, 101

Потоки, программные каналы и перенаправления

Изучение основ работы с конвейерами Linux

Серия контента:

Краткий обзор

Из этой статьи вы узнаете об основных приемах перенаправления стандартных потоков ввода/вывода в Linux. Вы научитесь:

  • Перенаправлять стандартные потоки ввода/вывода: стандартный поток ввода, стандартный поток вывода и стандартный поток ошибок.
  • Направлять вывод одной команды на вход другой команды.
  • Отправлять вывод одновременно на стандартное устройство вывода (stdout) и в файл.
  • Использовать вывод команды в качестве аргументов другой команды.

Эта статья поможет вам подготовиться к сдаче экзамена LPI 101 на администратора начального уровня (LPIC-1) и содержит материалы цели 103.4 темы 103. Цель имеет вес 4.

Об этой серии

Эта серия статей поможет вам освоить задачи администрирования операционной системы Linux. Вы также можете использовать материал этих статей для подготовки к .

Чтобы посмотреть описания статей этой серии и получить ссылки на них, обратитесь к нашему . Этот перечень постоянно дополняется новыми статьями по мере их готовности и содержит самые последние (по состоянию на апрель 2009 года) цели экзаменов сертификации LPIC-1. Если какая-либо статья отсутствует в перечне, можно найти ее более раннюю версию, соответствующую предыдущим целям LPIC-1 (до апреля 2009 года), обратившись к нашим .

Необходимые условия

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

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

Как связаться с Яном

Ян – один из наших наиболее популярных и плодовитых авторов. Ознакомьтесь со (EN), опубликованными на сайте developerWorks. Вы можете найти контактные данные в и связаться с ним, а также с другими авторами и участниками ресурса My developerWorks.

Для выполнения примеров в этой статье мы будем использовать некоторые файлы, созданные ранее в статье " ". Если вы не читали эту статью или не сохранили эти файлы, не расстраивайтесь! Давайте начнем с создания новой директории lpi103-4 и всех необходимых файлов. Для этого откройте текстовое окно и перейдите в вашу домашнюю директорию. Скопируйте содержимое листинга 1 в текстовое окно; в результате выполнения команд в вашей домашней директории будет создана поддиректория lpi103-4 и все необходимые файлы в ней, которые мы и будем использовать в наших примерах.

Листинг 1. Создание файлов, необходимых для примеров этой статьи
mkdir -p lpi103-4 && cd lpi103-4 && { echo -e "1 apple\n2 pear\n3 banana" > text1 echo -e "9\tplum\n3\tbanana\n10\tapple" > text2 echo "This is a sentence. " !#:* !#:1->text3 split -l 2 text1 split -b 17 text2 y; }

Ваше окно должно выглядеть так, как показано в листинге 2, а вашей текущей рабочей директорией должна стать вновь созданная директория lpi103-4.

Листинг 2. Результаты создания необходимых файлов
$ mkdir -p lpi103-4 && cd lpi103-4 && { > echo -e "1 apple\n2 pear\n3 banana" > text1 > echo -e "9\tplum\n3\tbanana\n10\tapple" > text2 > echo "This is a sentence. " !#:* !#:1->text3echo "This is a sentence. " "This is a sentence. " "This is a sentence.">text3 > split -l 2 text1 > split -b 17 text2 y; } $

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

Командный интерпретатор Linux, такой как Bash, получает входные данные и направляет выходные данные в виде последовательностей или потоков символов. Любой символ не зависит ни от предыдущих, ни от последующих символов. Символы не упорядочены в виде структурированных записей или блоков с фиксированным размером. Доступ к потокам осуществляется с помощью механизмов ввода/вывода независимо от того, откуда поступают и куда передаются потоки символов (файл, клавиатура, окно, экран или другое устройство ввода/вывода). Командные интерпретаторы Linux используют три стандартных потока ввода/вывода, каждому из которых назначен определенный файловый дескриптор.

  1. stdout стандартный поток вывода , отображает вывод команд и имеет дескриптор 1.
  2. stderr стандартный поток ошибок , отображает ошибки команд и имеет дескриптор 2.
  3. stdin стандартный поток ввода , передает входные данные командам и имеет дескриптор 0.

Потоки ввода обеспечивают передачу входных данных (обычно поступающих с клавиатуры) командам. Потоки вывода обеспечивают печать текстовых символов, как правило, на терминал. Изначально терминал представлял собой ASCII печатающее устройство или дисплейный терминал, но сейчас, как правило, это просто окно на рабочем столе компьютера.

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

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

Существует два способа перенаправления вывода в файл:

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

Символ n в операторах n> или n>> является файловым дескриптором . Если он не указан, то предполагается, что используется стандартное устройство вывода. В листинге 3 продемонстрирована операция перенаправления для разделения стандартного потока вывода и стандартного потока ошибок команды ls с использованием файлов, которые были созданы ранее в директории lpi103-4. Также продемонстрировано добавление вывода команды в существующие файлы.

Листинг 3. Перенаправление вывода
$ ls x* z* ls: cannot access z*: No such file or directory xaa xab $ ls x* z* >stdout.txt 2>stderr.txt $ ls w* y* ls: cannot access w*: No such file or directory yaa yab $ ls w* y* >>stdout.txt 2>>stderr.txt $ cat stdout.txt xaa xab yaa yab $ cat stderr.txt ls: cannot access z*: No such file or directory ls: cannot access w*: No such file or directory

Мы уже говорили, что перенаправление вывода с помощью оператора n> обычно приводит к перезаписи существующих файлов. Вы можете управлять этим свойством при помощи опции noclobber встроенной команды set . Если эта опция определена, то вы можете переопределить ее с помощью оператора n>|, как показано в листинге 4.

Листинг 4. Перенаправление вывода с помощью опции noclobber
$ set -o noclobber $ ls x* z* >stdout.txt 2>stderr.txt -bash: stdout.txt: cannot overwrite existing file $ ls x* z* >|stdout.txt 2>|stderr.txt $ cat stdout.txt xaa xab $ cat stderr.txt ls: cannot access z*: No such file or directory $ set +o noclobber #restore original noclobber setting

Иногда может потребоваться перенаправить в файл как стандартный вывод, так и стандартный поток ошибок. Часто это используется в автоматизированных процессах или фоновых заданиях для того, чтобы впоследствии можно было просмотреть результаты работы. Чтобы перенаправить стандартный вывод и стандартный поток ошибок в одно и то же место, используйте оператор &> или &>>. Альтернативный вариант – перенаправить файловый дескриптор n и затем файловый дескриптор m в одно и то же место с помощью конструкции m>&n или m>>&n. В этом случае важен порядок перенаправления потоков. Например, команда
command 2>&1 >output.txt
это не то же самое, что команда
command >output.txt 2>&1
В первом случае поток ошибок stderr перенаправляется в текущее месторасположение потока stdout, а затем поток stdout перенаправляется в файл output.txt; однако второе перенаправление затрагивает только stdout, но не stderr. Во втором случае поток stderr перенаправляется в текущее месторасположение потока stdout, то есть, в файл output.txt. Эти перенаправления проиллюстрированы в листинге 5. Обратите внимание на последнюю команду, в которой стандартный вывод был перенаправлен после стандартного потока ошибок, и, как следствие, поток ошибок продолжает выводиться в окно терминала.

Листинг 5. Перенаправление двух потоков в один файл
$ ls x* z* &>output.txt $ cat output.txt ls: cannot access z*: No such file or directory xaa xab $ ls x* z* >output.txt 2>&1 $ cat output.txt ls: cannot access z*: No such file or directory xaa xab $ ls x* z* 2>&1 >output.txt # stderr does not go to output.txt ls: cannot access z*: No such file or directory $ cat output.txt xaa xab

В других ситуациях вам может потребоваться полностью проигнорировать стандартный вывод или стандартный поток ошибок. Для этого следует перенаправить соответствующий поток в пустой файл /dev/null. В листинге 6 показано, как проигнорировать поток ошибок команды ls и как с помощью команды cat убедиться в том, что файл /dev/null на самом деле пуст.

Листинг 6. Игнорирование стандартного потока ошибок посредством использования /dev/null
$ ls x* z* 2>/dev/null xaa xab $ cat /dev/null

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

Так же, как мы можем перенаправить потоки stdout и stderr, мы можем перенаправить поток stdin из файла с помощью оператора <. Если вы прочли руководство " ", то должны помнить, что в разделе была использована команда tr для замены пробелов в файле text1 на символы табуляции. В том примере мы использовали вывод команды cat чтобы создать стандартный поток ввода для команды tr . Теперь для преобразования пробелов в символы табуляции вместо бесполезного вызова команды cat мы можем использовать перенаправление ввода, как показано в листинге 7.

Листинг 7. Перенаправление ввода
$ tr " " "\t"

В командных интерпретаторах, в том числе и в bash, реализована концепция here-document , которая является одним из способов перенаправления ввода. В ней используется конструкция << и какое-либо слово, например END, являющееся маркером, или сигнальной меткой, означающей конец ввода. Эта концепция продемонстрирована в листинге 8.

Листинг 8. Перенаправление ввода с использованием концепции here-document
$ sort -k2 < 1 apple > 2 pear > 3 banana > END 1 apple 3 banana 2 pear

Но почему нельзя просто набрать команду sort -k2 , ввести данные и нажать комбинацию Ctrl-d , означающую конец ввода? Разумеется, вы могли бы выполнить эту команду, но тогда вы не узнали бы о концепции here-document, которая очень часто используется в сценариях командной оболочки (в которых не существует другой возможности указать, какие именно строки должны восприниматься в качестве ввода). Поскольку для выравнивания текста и обеспечения удобства чтения в сценариях широко используются символы табуляции, существует другой прием использования концепции here-document. При использовании оператора <<- вместо оператора << начальные символы табуляции удаляются.

В листинге 9 мы использовали подстановку команд для создания символа табуляции, а затем создали небольшой сценарий командной оболочки, содержащий две команды cat , каждая из которых считывает данные из блока here-document. Заметьте, что мы использовали слово END в качестве сигнальной метки блока here-document, который мы считываем с терминала. Если бы мы использовали это же слово в нашем сценарии, то наш ввод закончился бы преждевременно. Поэтому вместо слова END мы используем в сценарии слово EOF. После того, как наш сценарий создан, мы используем команду. (точка) чтобы запустить его в контексте текущего командного интерпретатора.

Листинг 9. Перенаправление ввода с использованием концепции here-document
$ ht=$(echo -en "\t") $ cat<ex-here.sh > cat <<-EOF > apple > EOF > ${ht}cat <<-EOF > ${ht}pear > ${ht}EOF > END $ cat ex-here.sh cat <<-EOF apple EOF cat <<-EOF pear EOF $ . ex-here.sh apple pear

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

Создание конвейеров

Использование команды xargs

Команда xargs считывает данные со стандартного устройства ввода, а затем строит и выполняет команды, параметрами которых являются полученные входные данные. Если не указана никакая команда, то используется команда echo . В листинге 12 приведен простой пример использования нашего файла text1, содержащего три строки по два слова в каждой.

Листинг 12. Использование команды xargs
$ cat text1 1 apple 2 pear 3 banana $ xargs

Почему же тогда вывод xargs содержит только одну строку? По умолчанию xargs разбивает входные данные, если встречает символы-разделители, и каждый полученный фрагмент становится отдельным параметром. Однако когда xargs строит команду, ей за один раз передается максимально возможное количество параметров. Это поведение можно изменить с помощью параметра –n или --max-args . В листинге 13 приведен пример использования обоих вариантов; также был выполнен явный вызов команды echo для использования с xargs .

Листинг 13. Использование команд xargs и echo
$ xargs" args > 1 apple 2 pear 3 banana $ xargs --max-args 3 " args > 1 apple 2 args > pear 3 banana $ xargs -n 1 " args > 1 args > apple args > 2 args > pear args > 3 args > banana

Если входные данные содержат пробелы, но при этом они заключены в одиночные или двойные кавычки (либо пробелы представлены в виде escape-последовательностей с использованием обратной косой черты), то xargs не будет разбивать их на отдельные части. Это показано в листинге 14.

Листинг 14. Использование команды xargs и кавычек
$ echo ""4 plum"" | cat text1 - 1 apple 2 pear 3 banana "4 plum" $ echo ""4 plum"" | cat text1 - | xargs -n 1 1 apple 2 pear 3 banana 4 plum

До сих пор все аргументы добавлялись в конец команды. Если вам необходимо, чтобы после них были добавлены другие дополнительные аргументы, то воспользуйтесь опцией -I для указания строки замещения. В том месте вызываемой через xargs команды, в котором используется строка замещения, вместо нее будет подставлен аргумент. При использовании такого подхода каждой команде передается только один аргумент. Однако аргумент будет создан из целой входной строки, а не из отдельного ее фрагмента. Также вы можете использовать опцию -L команды xargs , в результате чего в качестве аргумента будет использоваться вся строка целиком, а не отдельные ее фрагменты, разделенные пробелами. Использование опции -I неявно вызывает использование опции -L 1 . В листинге 15 приведены примеры использования опций -I и –L .

Листинг 15. Использование команды xargs и строк ввода
$ xargs -I XYZ echo "START XYZ REPEAT XYZ END" " <9 plum> <3 banana><3 banana> <10 apple><10 apple> $ cat text1 text2 | xargs -L2 1 apple 2 pear 3 banana 9 plum 3 banana 10 apple

Хотя в наших примерах используются простые текстовые файлы, вы не будете часто использовать команду xargs для таких случаев. Как правило, вы будете иметь дело с большим списком файлов, полученных в результате выполнения таких команд, как ls , find или grep . В листинге 16 показан один из способов передачи через xargs списка содержимого директории такой команде, как, например, grep .

Листинг 16. Использование команды xargs и списка файлов
$ ls |xargs grep "1" text1:1 apple text2:10 apple xaa:1 apple yaa:1

Что произойдет в последнем примере, если одно или несколько имен файлов будут содержать пробелы? Если вы попытаетесь использовать команду так, как это было сделано в листинге 16, то вы получите ошибку. В реальной ситуации список файлов может быть получен не от команды ls , а, например, в результате выполнения пользовательского сценария или команды; а может быть, вы захотите обработать его на других этапах конвейера с целью дополнительной фильтрации. Поэтому мы не берем во внимание тот факт, что вы могли бы просто использовать команду grep "1" * вместо существующей логической структуры.

В случае с командой ls вы могли бы использовать опцию --quoting-style для того, чтобы имена файлов, содержащие пробелы, были заключены в скобки (или представлены в виде escape-последовательностей). Лучшим решением (когда это возможно) является использование опции -0 команды xargs , в результате чего для разделения входных аргументов используются пустые символы (\0). Хотя команда ls не имеет опции, позволяющей использовать в качестве вывода имена файлов с завершающим нулем, многие команды умеют делать это.

В листинге 17 мы сначала скопируем файл text1 в "text 1", а затем приведем несколько примеров использования списка имен файлов, содержащих пробелы, с командой xargs . Эти примеры позволяют понять саму идею, поскольку полностью освоить работу с xargs может оказаться не так просто. В частности, последний пример преобразования символов новой строки в пустые символы не сработал бы в том случае, если некоторые имена файлов уже содержали бы символы новой строки. В следующем разделе этой статьи мы рассмотрим более надежное решение с применением команды find для генерации подходящего вывода, в котором в качестве разделителей используются пустые символы.

Листинг 17. Использование команды xargs и файлов, содержащих пробелы в именах
$ cp text1 "text 1" $ ls *1 |xargs grep "1" # error text1:1 apple grep: text: No such file or directory grep: 1: No such file or directory $ ls --quoting-style escape *1 text1 text\ 1 $ ls --quoting-style shell *1 text1 "text 1" $ ls --quoting-style shell *1 |xargs grep "1" text1:1 apple text 1:1 apple $ # Illustrate -0 option of xargs $ ls *1 | tr "\n" "\0" |xargs -0 grep "1" text1:1 apple text 1:1 apple

Команда xargs не может строить сколь угодно длинные команды. Так, в Linux до версии ядра 2.26.3 максимальная длина команды была ограничена. Если вы попытаетесь выполнить такую команду, как, например, rm somepath/* , а директория содержит множество файлов с длинными именами, то выполнение может завершиться ошибкой, сообщающей, что список аргументов слишком длинный. Если вы работаете с более старыми версиями Linux или UNIX, в которых могут присутствовать такие ограничения, то будет полезно узнать, как можно использовать xargs таким образом, чтобы обойти их.

Вы можете использовать опцию --show-limits для просмотра ограничений, установленных по умолчанию для команды xargs , и опцию -s – для задания максимальной длины выводимых команд. Об остальных опциях вы можете узнать из man-страниц.

Использование команды find с опцией -exec или совместно с командой xargs

Из руководства " " вы узнали о том, как использовать команду find для поиска файлов на основе их имен, времени модификации, размера и прочих характеристик. Обычно над найденными файлами необходимо выполнять определенные действия – удалять, копировать, переименовывать их и так далее. Сейчас мы рассмотрим опцию -exec команды find , работа которой похожа на работу команды find с последующей передачей вывода команде xargs .

Листинг 18. Использование команды find с опцией -exec
$ find text -exec cat text3 {} \; This is a sentence. This is a sentence. This is a sentence. 1 apple 2 pear 3 banana This is a sentence. This is a sentence. This is a sentence. 9 plum 3 banana 10 apple

Сопоставив результаты листинга 18 с тем, что вам уже известно о xargs , можно обнаружить несколько различий.

  1. Вы должны использовать в команде символы {} для указания места подстановки, в которое будет подставлено имя файла. Эти символы не добавляются автоматически в конце команды.
  2. Вы должны завершить команду точкой с запятой, которая должна быть представлена в виде escape-последовательности (\;, ";" или ";").
  3. Команда выполняется один раз для каждого входного файла.

Попробуйте самостоятельно выполнить команду find text |xargs cat text3 , чтобы увидеть различия.

Теперь давайте вернемся к случаю, когда имя файла содержит пробелы. В листинге 19 мы попытались использовать команду find с опцией -exec вместо команд ls и xargs .

Листинг 19. Использование команды find с опцией -exec и файлов, содержащих пробелы в именах
$ find . -name "*1" -exec grep "1" {} \; 1 apple 1 apple

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

В этой ситуации мы могли бы воспользоваться командой xargs , однако мы уже знаем о проблеме с файлами, имена которых содержат пробелы. Также мы упоминали тот факт, что команда find может генерировать список имен с пустыми разделителями, благодаря опции -print0 . Современные версии команды find могут разделяться не точкой с запятой, а знаком +, благодаря чему, за один вызов команды find можно передать максимально возможное число имен, так же, как и в случае использования xargs . Излишне говорить о том, что в этом случае вы можете использовать конструкцию {} только один раз, и что она должна являться последним параметром команды. В листинге 20 продемонстрированы оба этих метода.

Листинг 20. Использование команд find , xargs и файлов, содержащих пробелы в именах
$ find . -name "*1" -print0 |xargs -0 grep "1" ./text 1:1 apple ./text1:1 apple $ find . -name "*1" -exec grep "1" {} + ./text 1:1 apple ./text1:1 apple

Оба этих метода являются рабочими и выбор какого-то одного из них часто обусловлен лишь личными предпочтениями пользователя. Помните о том, что передавая по конвейеру объекты с необработанными символами-разделителями и пробелами, вы можете столкнуться с проблемами; поэтому если вы передаете вывод команде xargs , то используйте опцию -print0 команды find , а также опцию -0 команды xargs , которая сообщает, что во входных данных используются пустые разделители. Другие команды, включая tar , также поддерживают опцию -0 и работу с входными данными, содержащими пустые разделители, поэтому всегда следует использовать эту опцию для тех команд, которые ее поддерживают, если только вы уверены на все 100%, что входной список не создаст вам проблем.

Наш последний комментарий затрагивает работу со списком файлов. Хорошей идеей будет всегда тщательно проверять ваш список и команды, прежде чем выполнять пакетные операции (например, удаление или переименование множества файлов). Наличие актуальной резервной копии в нужный момент также может оказаться неоценимым.

Разделение вывода

В завершение этой статьи мы кратко рассмотрим еще одну команду. Иногда может возникнуть необходимость просматривать вывод на экране и одновременно сохранять его в файл. Для этого вы могли бы перенаправить вывод команды в файл в одном окне, а затем с помощью tail -fn1 отслеживать вывод в другом окне, однако проще всего использовать команду tee .

Команда tee используется в конвейере, а ее аргументом является имя файла (или имена нескольких файлов), в который будет передаваться стандартный вывод. Опция -a позволяет не замещать старое содержимое файла новым содержимым, а добавлять данные в конец файла. Как уже говорилось при рассмотрении конвейеризации, если вы хотите сохранить как стандартный вывод, так и поток ошибок, то необходимо перенаправлять поток stderr в поток stdout прежде, чем передавать данные на вход команде tee . В листинге 21 приведен пример использования команды tee для сохранения вывода в два файла, f1 и f2.

Листинг 21. Разделение потока stdout с помощью команды tee
$ ls text|tee f1 f2 text1 text2 text3 $ cat f1 text1 text2 text3 $ cat f2 text1 text2 text3

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

  • Отображение выводимых данных на экране.
  • Перенаправление вывода в файл.
Иногда что-то надо показать на экране, а что-то - записать в файл, поэтому нужно разобраться с тем, как в 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, о том, как обрабатывать их в сценариях, о запуске заданий по расписанию и о фоновых задачах.

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

Встроенные возможности перенаправления в 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:


Top