Глиф Учебники

Урок 5. Перенаправление вывода и конвейеры

Курс «Командная строка Linux» · урок 5 из 10 · 4 минуты чтения

Главная сила командной строки — в соединении простых команд. Одна выбирает строки, другая сортирует, третья считает, и вместе они за секунду делают то, на что в окнах ушёл бы час. Для этого нужно понять, куда команды пишут результат и откуда читают данные.

Три потока

У каждой команды есть три стандартных потока:

НомерИмяЧто это
0stdin, вводоткуда команда читает данные; обычно — клавиатура
1stdout, выводкуда пишет результат; обычно — экран
2stderr, ошибкикуда пишет сообщения об ошибках; тоже экран

На экране вывод и ошибки выглядят одинаково, но это два разных потока, и направлять их можно по отдельности.

Вывод в файл: > и >>

Bash
ls /etc > list.txt
date >> list.txt

> стирает файл молча, без вопроса. sort data.txt > data.txt оставит пустой файл: оболочка очищает файл ещё до того, как sort начнёт его читать. Пишите результат в новый файл.

Защита от случайной перезаписи — команда set -o noclobber: после неё > откажется писать в существующий файл.

Ошибки отдельно: 2>

Bash
ls /etc /nonexistent > out.txt 2> errors.txt
find / -name "*.conf" 2>/dev/null
ls /etc /nonexistent > all.txt 2>&1
ls /etc /nonexistent &> all.txt
ls /etc /nonexistent > out.txt 2> errors.txt клавиатура программа ls 0 ввод stdin 1 вывод stdout 2 ошибки stderr > 2> out.txt список /etc errors.txt нет /nonexistent без перенаправления: ввод — с клавиатуры, вывод и ошибки — на экран
У команды три потока: ввод 0 с клавиатуры, вывод 1 и ошибки 2; знак > отправляет вывод в out.txt, а 2> — ошибки в errors.txt.

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

ls /etc /nonexistent > all.txt 2>&1 всё в файле сначала 1 → экран 2 → экран после > all.txt 1 → all.txt 2 → экран после 2>&1 1 → all.txt 2 → all.txt ls /etc /nonexistent 2>&1 > all.txt ошибки на экране сначала 1 → экран 2 → экран после 2>&1 1 → экран 2 → экран после > all.txt 1 → all.txt 2 → экран 2>&1 — ошибки туда, куда сейчас идёт вывод; читается слева направо
При записи > all.txt 2>&1 сначала вывод уходит в файл, а потом ошибки следуют за ним; при 2>&1 > all.txt ошибки остаются на экране, куда шёл вывод в момент 2>&1.

Ввод из файла: <

Bash
wc -l < list.txt

< подаёт файл на вход команде вместо клавиатуры. Похожая конструкция — here-document: строки прямо из команды или скрипта, до строки-метки:

Bash
cat > hello.txt << 'EOF'
Первая строка
Вторая строка
EOF

Всё между << 'EOF' и строкой EOF попадает на вход cat, а та пишет его в файл. Кавычки вокруг EOF запрещают подстановку переменных внутри.

Конвейер: |

Знак | (pipe, труба) передаёт вывод одной команды на вход следующей:

Bash
ls /etc | wc -l
history | grep ssh
ps aux | grep nginx
ls -l /usr/bin | less

Первая строка считает файлы в /etc, вторая ищет в истории команды с ssh, третья — процессы nginx, четвёртая позволяет листать длинный вывод. По трубе идёт только вывод; чтобы туда попали и ошибки, пишут 2>&1 |.

Считать и сортировать: wc, sort, uniq

Создайте файл со списком городов — по одному в строке:

Bash
printf '%s\n' минск гомель минск брест минск гомель > cities.txt
Терминал
$ wc -l cities.txt
6 cities.txt
$ sort cities.txt
брест
гомель
гомель
минск
минск
минск
$ sort -u cities.txt
брест
гомель
минск
$ sort cities.txt | uniq -c
      1 брест
      2 гомель
      3 минск
$ sort cities.txt | uniq -c | sort -rn
      3 минск
      2 гомель
      1 брест
sort cities.txt | uniq -c | sort -rn cities.txt минск гомель минск брест минск гомель брест гомель гомель минск минск минск 1 брест 2 гомель 3 минск 3 минск 2 гомель 1 брест sort uniq -c sort -rn чаще всего — сверху
В конвейере sort cities.txt | uniq -c | sort -rn строки сначала сортируются, одинаковые встают рядом и считаются, а затем самый частый город поднимается наверх.

Последняя цепочка — частый приём: «что встречается чаще всего». Так, например, ищут адреса, с которых больше всего запросов к сайту:

Bash
cut -d ' ' -f 1 /var/log/nginx/access.log | sort | uniq -c | sort -rn | head -n 10

cut -d ' ' -f 1 оставляет из каждой строки только первое поле до пробела — адрес посетителя; head -n 10 — десять первых строк результата.

Вывод и на экран, и в файл: tee

Bash
ping -c 4 debian.org | tee ping.txt
echo "192.168.1.10 server" | sudo tee -a /etc/hosts

tee пишет полученное в файл и одновременно показывает на экране; -a — дописать, а не перезаписать.

Вторая строка решает частую загадку: sudo echo "…" >> /etc/hosts не работает, потому что >> выполняет ещё ваша оболочка без прав root, а sudo относится только к echo. sudo tee открывает файл уже с правами root.

Несколько команд в строке: ; && ||

Bash
cd ~/practice; ls
mkdir backup && cp *.txt backup/
ping -c 1 192.168.1.1 > /dev/null || echo "Роутер не отвечает"

Коротко

Проверьте себя

1. Что будет с файлом log.txt после команды date > log.txt, если в нём уже были строки?
2. Как записать в один файл и вывод команды, и её ошибки?
3. Почему перед uniq -c обычно ставят sort?
4. Почему не работает sudo echo "текст" >> /etc/hosts?