Потоки

Теоретическая часть

Стандартные потоки ввода и вывода в UNIX/Linux наряду с файлами являются одним из наиболее распространённых средств для обмена информацией процессов с внешним миром, а перенаправления >, >> и |, одной из самых популярных конструкций командного интерпретатора.

На этой странице рассматриваются как базовые вопросы использования потоков ввода/вывода, так и тонкости и хитрости, например, почему не работает echo text | read ver и многие другие.

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

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


Рассмотрим запись звука в файл с помощью команды:

cat /dev/audio > /tmp/my.sound

Эта команда читает файл (устройство) /dev/audio с помощью команды cat и перенаправляет информацию из него в файл /tmp/my.sound (с помощью оператора >).

У каждой программы существует 3 системных потока: stdout, stderr, stdin.

stdout

Стандартный поток вывода данных для программ. Например, когда мы пишем команду ls, то список папок и файлов она выводит именно в этот поток, который отображается у нас в консоли:

$ ls
bin incoming pub usr

stderr

Поток вывода ошибок. Если программа не смогла сделать все как надо — она пишет именно в этот поток. Например, когда rm пытается удалить несуществующий файл:

$ rm example.txt
rm: example.txt: No such file or directory

stdin

Поток ввода данных. А вот это довольно интересный и удобный поток. Например, его использует веб-сервер, когда просит интерпретаторы выполнить скрипты через CGI. Мы тоже можем попробовать:

$ echo '<?php echo «Hello world»; ?>' | php Hello world

В этом примере мы встретили оператор перенаправления потока вывода.


Теперь познакомимся с перенаправлением потоков.

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

Для начала рассмотрим перенаправление потоков в файлы, устройства и другие потоки.

$ ls >1.txt

В этом примере мы направили stdout команды ls в файл 1.txt. Читаем его:

$ cat 1.txt
bin incoming pub usr

Да, все успешно записалось.

Теперь попробуем направить stderr команды rm:

$ rm example.txt 2>1.txt

Здесь мы использовали номер потока stderr (2). По умолчанию оператор > перенаправляет поток stdout, который имеет номер 1. Чтобы направить другой поток, надо перед оператором > поставить его номер.

Мы можем направлять одни потоки в направлении других:

$ rm exmple.txt >1.txt 2>&1

В этом примере мы направили поток stdout в файл 1.txt, а затем направили stderr туда же, куда направлен stdout с помощью оператора & перед номером потока.

Теперь давайте поиграем с потоком stdin. Например, я хочу найти все папки ".svn" в некотором проекте и удалить:

cd myproject
find .

Команда find с параметром. выводит в stdout все вложенные папки и файлы, которые находит в данной папке и во всех вложенных.

Теперь нам надо выбрать только папки с именем ".svn":

find . | grep -e '/.svn$'

Оператор | перенаправляет stdout одного приложения в stdin следующего. То есть все строки найденные с помощью find пошли в команду grep, которая выбирает строки по определенным условиям и выводит их.

Примечание

Здесь условие — это регулярное выражение, которое говорит о том, что строка должна заканчиваться на "/.svn".

Нужные папки мы выбрали, осталось их удалить.

rm -Rf `find . | grep -e '/.svn$'`

И снова новый оператор: `. Он забирает stdout из команды, которую он окружает и вставляет в данное место как строку.

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

find . | grep -e '/.svn$' | xargs rm -Rf

Теперь мы отдаем нужные файлы команде xargs, которая вызывает rm -Rf и в качестве параметров использует свой stdin построчно. Задача решена.

Практическая часть

Задачи

  1. Создайте файл с именем 1.txt и содержанием newfile (скопировать -- вставить).
  2. Использование какой команды дает возможность одновременно увидеть и сохранить выходные данные (промежуточное звено между stdout и stdin)?
  3. Перенаправьте STDERR в файл. Через какой поток будет происходить перенаправление?
  4. Напишите код перенаправления обоих потоков в файл.
  5. Чем выгоден вышеупомянутый способ перенаправления потоков в файл?
  6. (*) Сожмите файл 1.txt
  7. (*) Теперь распакуйте файл 1.txt