Три потока
До сих пор каждая команда жила сама по себе: вы её запускали, она писала результат в окно. Но настоящая сила терминала — в том, что результат можно направить куда угодно: в файл, в другую команду, в никуда. Чтобы понять, как это устроено, нужно одно понятие — потоки.
У каждой команды три потока:
- вход (номер 0) — откуда она читает данные, если ей не дали файл. По умолчанию — клавиатура;
- выход (номер 1) — куда пишет результат. По умолчанию — окно терминала;
- ошибки (номер 2) — куда пишет сообщения о проблемах. По умолчанию — тоже окно.
Разделение выхода и ошибок кажется лишним, пока не понадобится сохранить результат в файл: тогда удобно, что в файл идёт только полезное, а ошибки остаются видны на экране. И наоборот — можно собрать только ошибки, чтобы понять, что пошло не так.
Номера потоков — 0, 1 и 2 — понадобятся в следующей главе: по ним оболочке говорят, какой именно поток куда направить.
Перенаправление
Перенаправление меняет, куда пишет или откуда читает команда. Знаков немного, и каждый стоит запомнить точно.
| Запись | Что делает |
|---|---|
команда > файл | выход в файл; файл перезаписывается |
команда >> файл | выход в конец файла, старое сохраняется |
команда 2> файл | только ошибки в файл |
команда > файл 2>&1 | и выход, и ошибки в один файл |
команда < файл | взять вход из файла |
ls -l > list.txt # список в файл (старое содержимое пропадёт)
date >> journal.txt # дописать строку в конец
find / -name "*.conf" 2> /dev/null # ошибки «нет доступа» — в никуда
/dev/null — особый файл-«чёрная дыра»: всё, что в него пишется, исчезает. Туда направляют то, что не нужно видеть, — чаще всего сообщения об ошибках, которые заранее ожидаемы. Например, find по всему диску засыпает экран строками «нет доступа», а 2> /dev/null убирает их и оставляет только найденное.
Запись 2>&1 читается как «поток 2 направить туда же, куда сейчас направлен поток 1». Порядок важен: сначала указывают, куда идёт выход, потом приклеивают к нему ошибки.
Конвейер
Конвейер — вертикальная черта | между командами. Выход левой становится входом правой. Так из маленьких команд собирается решение.
ls -l | less # длинный список — постранично
grep error app.log | wc -l # сколько строк с ошибками
history | grep ssh # какие команды ssh я набирал
Несколько команд, которые почти всегда живут внутри конвейеров:
| Команда | Что делает |
|---|---|
sort | упорядочивает строки; -n — как числа, -r — наоборот |
uniq -c | склеивает одинаковые соседние строки и считает их |
cut -d' ' -f1 | вырезает первый столбец по разделителю «пробел» |
head / tail | первые или последние строки результата |
wc -l | сколько строк получилось |
Вот знаменитый пример: кто чаще всех обращался к сайту. В журнале веб-сервера первый столбец — адрес посетителя.
cut -d' ' -f1 access.log | sort | uniq -c | sort -rn | head -5
1843 203.0.113.7
912 198.51.100.4
402 192.0.2.15
...
Читается слева направо: вырезать адреса, упорядочить, посчитать повторы, упорядочить по числу от большего, оставить пять первых. Пять простых команд — и ответ, для которого пришлось бы писать программу.
Приёмы и ловушки
Три приёма, которые часто нужны вместе с конвейерами, и одна ловушка.
tee — записать в файл и одновременно показать на экране. Удобно, когда длинную работу хочется и видеть, и сохранить:
./build.sh | tee build.log
Записать в системный файл. sudo echo "..." > /etc/файл не сработает: sudo получает только echo, а перенаправление выполняет ваша обычная оболочка без прав. Правильно — отдать запись команде tee, запущенной с правами:
echo "192.168.1.10 nas" | sudo tee -a /etc/hosts
Ловушка: файл сам в себя. Команда sort names.txt > names.txt оставит пустой файл. Перенаправление обнуляет names.txt раньше, чем sort успеет его прочитать. Результат записывают в новый файл, а потом, проверив, переименовывают.
Конвейеры — лучшее место, чтобы почувствовать философию Linux: маленькие инструменты, каждый делает одно, а вместе — что угодно. Когда задача кажется требующей отдельной программы, сначала стоит спросить: из каких простых шагов она складывается?