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