Конвейер и перенаправление в Linux: |, > и 2>&1

Linux Автор: Среда и версия: Ubuntu 24.04, bash 5.2, GNU coreutils 9.4
содержание

У каждой запущенной программы три стандартных потока: ввод stdin (номер 0), вывод stdout (1) и поток ошибок stderr (2). По умолчанию оба выходных потока печатаются в терминал вперемешку, поэтому кажется, что поток один. Перенаправление меняет, куда течёт каждый из них, а конвейер соединяет вывод одной команды со входом другой. На этих двух механизмах держатся все однострочники командной строки.

Linux / 01

2>&1 направляет stderr туда же, куда stdout

2>&1 направляет stderr туда же, куда stdout01 / вход 02 / операция 03 / результат command > out.log stdout / 1 out.log затем 2>&1 stderr / 2 туда же: out.log терминал оба потока уведены вывода нет01 / вход02 / операция03 / результатcommand > out.logstdout / 1out.logзатем 2>&1stderr / 2туда же: out.logтерминалоба потока уведенывывода нет
> out.log уводит в файл только stdout. 2>&1 отправляет stderr «туда же, куда сейчас смотрит поток 1» — и в терминале не остаётся ничего.

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

Знак > направляет stdout в файл вместо терминала. Одинарный перезаписывает файл, двойной дописывает в конец:

echo "молоко" > shopping.txt
echo "хлеб" >> shopping.txt
cat shopping.txt
молоко
хлеб

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

Почему ошибки «просачиваются» мимо файла

Возьмём ls с двумя аргументами, один из которых не существует. На экране два сообщения вперемешку:

ls app missing
ls: cannot access 'missing': No such file or directory
app:
main.py

Теперь перенаправим вывод в файл:

ls app missing > files.txt
ls: cannot access 'missing': No such file or directory

Ошибка осталась на экране, потому что > забрал только stdout. Сообщение про missing шло по другому потоку — stderr, и его > не трогает. Это удобно: запускаешь долгую команду с выводом в файл, а ошибки видишь сразу. Но в логах нужны оба потока.

2>&1: собрать оба потока в один файл

Конструкция 2>&1 читается как «поток 2 отправить туда же, куда сейчас смотрит поток 1»:

ls app missing > all.log 2>&1
cat all.log
ls: cannot access 'missing': No such file or directory
app:
main.py

Терминал молчит, в файле всё. Порядок критичен: сначала > all.log разворачивает stdout в файл, потом 2>&1 присоединяет stderr. В обратном порядке — 2>&1 > all.log — stderr успевает нацелиться на терминал (куда stdout смотрел в тот момент) и остаётся там, а файл получает только stdout.

Обратный приём — спрятать ошибки совсем. /dev/null — специальный файл-«чёрная дыра», всё записанное в него исчезает:

ls app missing 2>/dev/null
app:
main.py

Так глушат заведомый шум — например, Permission denied при поиске find по всей системе.

Конвейер: вывод одной команды — вход другой

Знак | соединяет stdout левой команды со stdin правой. Классическая связка — частотный словарь. В файле tags.txt шесть строк с повторами:

sort tags.txt | uniq -c | sort -nr
      3 python
      2 sql
      1 linux

Три шага: sort собирает одинаковые строки рядом, uniq -c схлопывает соседние повторы и считает их, второй sort -nr сортирует по числу по убыванию. Каждая команда умеет одну вещь — конвейер собирает из них инструмент.

Вторая связка на каждый день — подсчёт: команда | wc -l печатает число строк вывода. А когда результат нужен и на экране, и в файле, есть tee:

echo "деплой прошёл" | tee deploy.log
деплой прошёл

Строка напечаталась и одновременно легла в deploy.log. С флагом -a tee дописывает, а не перезаписывает.

Ошибки, на которых спотыкаются

Перенаправление в тот же файл. sort list.txt > list.txt уничтожает данные: шелл очищает list.txt до запуска sort, и сортировать уже нечего — на выходе пустой файл. Сортируют во временный файл или используют sort -o list.txt list.txt.

2>&1 до перенаправления. command 2>&1 > out.log выглядит так же, но ошибки останутся на экране. Правильный порядок: сначала > out.log, потом 2>&1.

> вместо >> в скриптах-накопителях. Скрипт, который пишет лог через >, хранит только последний запуск. Для накопления — >>.

uniq без sort. uniq схлопывает только соседние повторы. На неотсортированном файле он молча оставит дубли, разбросанные по разным местам.

Частые вопросы

Чем > отличается от >>

> перезаписывает файл с нуля, >> дописывает в конец. Оба создают файл, если его не было.

Как записать в файл и вывод, и ошибки

command > out.log 2>&1. В bash есть короткая форма &> out.log — работает так же, но только в bash: в POSIX-скриптах (sh) она развалится на «запустить в фоне и перенаправить». Длинная форма надёжнее.

Что такое /dev/null

Псевдофайл, который молча поглощает всё записанное. 2>/dev/null прячет ошибки, >/dev/null — обычный вывод, >/dev/null 2>&1 — вообще всё. Читается из него только пустота.

Как посчитать, сколько строк в выводе команды

команда | wc -l. Например, ls | wc -l — сколько записей в каталоге, grep -r "TODO" . | wc -l — сколько пометок в проекте.

Где потренироваться

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

Источники