Конвейеры и перенаправление (pipes)

Материал из llmdocs — справочник разработчика
Конвейеры и перенаправление (pipes)
Идея конвейеровДуглас Макилрой (1964)
Реализация в UnixКен Томпсон, Unix Version 3 (1973)
Стандартные потоки0 — stdin, 1 — stdout, 2 — stderr
Операторы|, >, >>, <, 2>, 2>&1, &>, <<, <<<
Связанные механизмыtee, именованные каналы (mkfifo), подстановка процессов
Принципмаленькие программы, каждая делает одно дело

Конвейер (англ. pipe, «труба», в жаргоне — «пайп») — механизм Unix, который соединяет стандартный вывод одной программы со стандартным вводом другой: команда1 | команда2. Перенаправление связывает потоки ввода-вывода с файлами: команда > out.txt. Вместе они воплощают философию Unix: вместо одной большой программы, делающей всё, — маленькие утилиты, каждая из которых делает одно дело хорошо, соединяемые в цепочки.

Конвейеры — основа повседневной работы в командной строке Linux: найти в логе ошибки, отсортировать, посчитать, взять первые десять — одной строкой.

История

Идею соединять программы «как садовые шланги» высказал Дуглас Макилрой в служебной записке Bell Labs ещё в 1964 году. В 1973 году Кен Томпсон за одну ночь добавил в Unix системный вызов pipe() и синтаксис с вертикальной чертой, переписав утилиты так, чтобы они читали стандартный ввод и писали в стандартный вывод. Этот момент часто называют рождением «философии Unix».

Стандартные потоки

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

Номер Имя Назначение По умолчанию
0 stdin стандартный ввод клавиатура
1 stdout стандартный вывод экран
2 stderr поток ошибок экран

Разделение вывода и ошибок важно: при команда > file в файл уходят данные, а сообщения об ошибках по-прежнему видны на экране и не портят результат.

Перенаправления

ls > files.txt              # stdout в файл (перезаписать)
ls >> files.txt             # дописать в конец
sort < names.txt            # stdin из файла
make 2> errors.log          # только stderr в файл
cmd > out.log 2>&1          # оба потока в один файл (порядок важен!)
cmd &> out.log              # то же, сокращение Bash
cmd 2>/dev/null             # выбросить ошибки
cmd > /dev/null 2>&1        # выбросить всё

Запись 2>&1 означает «направить поток 2 туда же, куда сейчас направлен поток 1». Поэтому cmd 2>&1 > file отправит ошибки на экран, а в файл — только вывод: перенаправления применяются слева направо.[2]

Here-документ и here-строка

cat <<EOF > config.ini
[server]
host = $HOSTNAME
port = 8080
EOF

grep -c "a" <<< "banana"       # строка как stdin

Внутри <<EOF переменные раскрываются; чтобы передать текст буквально, ограничитель берут в кавычки: <<'EOF'.

Конвейеры

cat access.log | grep " 500 " | wc -l                  # число ошибок 500
ps aux | sort -k4 -rn | head -5                         # 5 процессов, потребляющих больше всего памяти
journalctl -u nginx --since today | grep -i error | less
du -sh * | sort -h | tail                               # самые большие каталоги
curl -s https://api.github.com/repos/python/cpython | jq '.stargazers_count'

Программы конвейера запускаются одновременно, и данные текут между ними порциями через буфер ядра (обычно 64 КБ в Linux), не накапливаясь целиком.[3] Если следующая программа читает медленнее, предыдущая ждёт; если она завершилась (как head), предыдущая получает сигнал SIGPIPE и тоже завершается (см. Процессы и сигналы). Поэтому head по огромному файлу работает мгновенно.

Коды выхода

Код выхода конвейера по умолчанию — код последней команды: false | true считается успехом. В скриптах это скрывает ошибки, поэтому используют set -o pipefail — тогда конвейер завершается неуспешно, если упала любая из команд. Коды всех команд доступны в массиве PIPESTATUS.[1]

Полезные приёмы

  • tee раздваивает поток: пишет и в файл, и дальше по конвейеру — make 2>&1 | tee build.log. Запись в файл с правами root: echo "..." | sudo tee /etc/file.
  • Подстановка процессов <(команда) подставляет вывод команды как временный файл: diff <(sort a.txt) <(sort b.txt).
  • Подстановка команд $(команда) вставляет вывод в строку: kill $(pgrep -f worker).
  • Именованные каналы (mkfifo /tmp/pipe) — конвейер в виде файла, через который могут общаться независимые процессы.[5]
  • xargs превращает поток строк в аргументы команды (см. find и xargs).

Классический пример

Подсчёт десяти самых частых слов в тексте — задача, на которой Макилрой в 1986 году показал силу конвейеров:

tr -cs '[:alpha:]' '\n' < book.txt | tr '[:upper:]' '[:lower:]' | sort | uniq -c | sort -rn | head

Каждая утилита проста: tr разбивает текст на слова и приводит к нижнему регистру, sort и uniq -c считают повторы, sort -rn упорядочивает, head берёт верхушку. Те же блоки решают и десятки других задач — для полей и сумм удобен awk, для поиска — grep, для JSON — jq.

Ограничения

Конвейеры передают неструктурированный текст: каждая программа заново разбирает строки, и пробелы в именах файлов или неожиданный формат вывода ломают цепочку. Для сложной логики и структурированных данных лучше подходят Python или оболочки нового поколения, передающие объекты (PowerShell, Nushell). Синтаксис конвейеров и перенаправлений закреплён в стандарте POSIX и одинаково работает в Bash, Zsh и dash.[4]

См. также

Источники

  1. GNU Bash manual — Pipelines
  2. GNU Bash manual — Redirections
  3. pipe(7) — Linux manual page
  4. POSIX.1-2024 — Shell Command Language
  5. mkfifo(1) — Linux manual page