Перенаправление ввода-вывода
Перенаправление ввода-вывода — форма межпроцессного взаимодействия, функция, присущая большинству интерпретаторов командной строки, в том числе различным оболочкам Unix, позволяющая перенаправлять стандартные потоки в указанные пользователем места. Концепция перенаправления достаточно стара и уходит корнями к самым ранним операционным системам. Уже в описании Multics 1971 года обсуждаются цели проектирования подсистемы ввода-вывода[1]. Однако до появления UNIX с поддержкой конвейеров (pipes), перенаправление ввода-вывода в ОС было сложно реализуемо или вовсе невозможно[2].
В Unix-подобных операционных системах программы реализуют перенаправление с помощью системного вызова dup2(2), а также его менее гибких, но более высокоуровневых аналогов из stdio — freopen(3) и popen(3)[3].
Перенаправление стандартных потоков ввода и вывода
Перенаправление обычно реализуется посредством специальных символов между командами.
Обычно синтаксис этих символов следующий: < используется для перенаправления ввода, а > — вывода. Команда command > file1 выполняет command, направляя вывод в file1 (обычно стандартный вывод отображается на терминал). При этом содержимое file1 будет перезаписано.
Применяя command < file1, команда command будет использовать в качестве источника ввода файл file1 вместо клавиатуры (которая обычно является стандартным источником ввода).
command < infile > outfile комбинирует оба подхода: command читает данные из infile, результат записывает в outfile.
Для добавления новых данных в конец файла (без перезаписи) используется оператор >>: command1 >> file1.
Для передачи текста на стандартный ввод в виде встроенного файла используется here-документ с оператором <<:
$ tr a-z A-Z << END_TEXT
> one two three
> uno dos tres
> END_TEXT
ONE TWO THREE
UNO DOS TRES
Чтобы читать из строки, применяют here-строку через оператор <<<: tr a-z A-Z <<< "one two three" или:
$ NUMBERS="one two three"
$ tr a-z A-Z <<< "$NUMBERS"
ONE TWO THREE
Конвейер
Программы могут работать вместе так, что одна программа получает на вход вывод другой, при этом явный промежуточный файл не требуется. command1 | command2 выполняет command1, используя его вывод как вход для command2 (называется конвейеризацией; символ «|» называют «пайпом»).
Две связанные таким образом программы могут работать в параллель, используя буфер обмена (в Linux его размер до 64 КБ), а также необходимое каждой из программ рабочее пространство. Например, команда сортировки («sort») не выдаст результат, пока не будут прочитаны все входные записи, так как последняя может оказаться первой в итоговой сортировке. В экспериментальной ОС Synthesis Алексии Масалин приоритет задач динамически менялся в зависимости от заполняемости их буферов ввода-вывода[4].
Этот подход аналогичен последовательному перенаправлению с использованием временного файла:
$ command1 > tempfile
$ command2 < tempfile
$ rm tempfile
Но в этом случае command2 запустится только после завершения command1, а для работы нужен достаточно большой временный файл. Например, хотя DOS позволяет использовать пайпы, фактически реализуется именно эта последовательная схема. Если некая долгая программа «Worker» выводит сообщения, а программа TimeStamp добавляет к каждой записи из stdin метку времени, то команда Worker | TimeStamp > LogFile.txt добавит отметку времени лишь после завершения Worker, отразив лишь быстроту чтения и записи итогового файла.
Типичный пример конвейеризации — комбинация echo с другой командой для эмуляции интерактивного ввода в скрипте, например: echo -e 'user\npass' | ftp localhost. Это запускает ftp-клиент с вводом user, затем return, затем pass.
В практическом использовании первым элементом конвейера часто бывает cat или echo, читающий из файла или строки. Их возможно заменить обычным перенаправлением входа или here-строкой, а комбинации cat и пайпа вместо редиректа называют бесполезным использованием cat (useless use of cat). Например, команды:
$ cat infile | command
$ echo $string | command
$ echo -e 'user\npass' | ftp localhost
можно заменить на:
$ command < infile
$ command <<< $string
$ ftp localhost <<< $'user\npass'
Поскольку echo — встроенная команда шелла, её использование критикуется меньше, чем cat, являющийся внешней командой.
Перенаправление стандартных файловых дескрипторов
В оболочках Unix, происходящих от оригинальной Bourne shell, к операторам редиректа можно добавить номер — файловый дескриптор, чтобы указать нужный поток[5]. Стандартные потоки Unix:[6]
| Дескриптор | Название | Описание |
|---|---|---|
| 0 | stdin | Стандартный ввод |
| 1 | stdout | Стандартный вывод |
| 2 | stderr | Стандартный вывод ошибок |
Например, command 2> file1 выполняет command, переправляя поток стандартных ошибок в файл file1.
В оболочках, производных от C shell, к перенаправляющему символу добавляется & (амперсанд), чтобы отличать вывод в файл с именем '1' от связанности с дескриптором stdout: например, cat file 2>1 (stderr направлен в файл «1») и cat file 2>&1 (stderr перенаправлен в stdout).
Дополнительно возможно перенаправить один файловый дескриптор к другому — чаще всего объединяют stderr и stdout, чтобы ошибки и данные шли вместе. Пример: find / -name .profile > results 2>&1 ищет все файлы .profile, а ошибки (например, из-за недоступных каталогов) добавляются к выводу в results.
Если объединённый вывод отправляется в другой процесс по пайпу, объединяющая запись 2>&1 должна предшествовать символу пайпа, например: find / -name .profile 2>&1 | less.
Более короткая, но не POSIX-совместимая форма command &>file или command >&file также объединяет stdout и stderr, однако доступна не во всех оболочках по умолчанию.
Возможна и следующая, но часто неверно понимаемая конструкция: если записать 2>&1 до «>», например, command 2>&1 > file, stdout будет записан в файл, а stderr останется связан с терминалом (так как был перенаправлен до переопределения stdout). Чтобы оба потока писались в файл, записи надо поменять местами: command > file 2>&1.
Сцепленные конвейеры
Символы перенаправления и пайпов можно комбинировать для составления сложных команд. Например, sort infile | uniq -c | sort -n > outfile сортирует строки файла infile, подсчитывает количество уникальных строк, отсортировывает результат по числу вхождений и сохраняет сумму в outfile[7]. Такой подход широко применяется в скриптах оболочки и батч-файлах.
Перенаправление в несколько мест назначения
Команда tee позволяет одновременно отправить вывод программы в несколько мест: ls -lrt | tee xyz — выводится список файлов как на экран, так и в файл xyz.
Примечания
Литература
- Feiertag, R. J.; Organick, E. I. (1972). “The Multics input/output system”. ACM SIGOPS Operating Systems Review [англ.]. 6 (1/2): 35—38. DOI:10.1145/850614.850622. ISSN 0163-5980.
- Kernighan, Brian W.; Morgan, Samuel P. (1982). “The UNIX Operating System: A Model for Software Design”. Science [англ.]. American Association for the Advancement of Science. 215 (4534): 779—783. ISSN 0036-8075. JSTOR 1687467. Дата обращения 2024-04-25.
Ссылки
dup: duplicate an open file descriptor — системные интерфейсы, The Single UNIX® Specification, выпуск 7 от The Open Group (англ.)- Определение Redirection на сайте The Linux Information Project (LINFO)
- Перенаправление ввода-вывода в The Linux Documentation Project
- Перенаправление в Windows
- Создание дочернего процесса с перенаправлением ввода-вывода в Windows