Конвейеры и фильтры — Примеры
Пример 1 — Сборка отчёта по одному этапу за раз
В файле журнала одна строка хранит один запрос, и седьмое её поле, разделённое пробелами, даёт эндпоинт. Нужны пять самых частых эндпоинтов с их количеством, причём ошибки не должны попасть в результат. Этапы ниже запускаются по одному, и про каждый нужно сказать, что он добавляет, а ещё объяснить, почему ошибки были отделены.
cut -d' ' -f7 access.log # один эндпоинт на строку
cut -d' ' -f7 access.log | sort # одинаковые эндпоинты становятся соседями
... | uniq -c # считает каждый блок одинаковых строк
... | sort -rn # самые частые первыми
... | head -5 # пять запрошенных
Решение
cut -d' ' -f7 access.log 2> errors.txt \
| sort | uniq -c | sort -rn | head -5
sort сближает одинаковые эндпоинты, чтобы uniq -c мог посчитать блок
uniq -c требует отсортированного ввода; на неотсортированном он даёт много мелких счётчиков
sort -rn упорядочивает по числу, которое напечатал uniq
2> errors.txt держит диагностику вне конвейера
Каждый этап делает одно небольшое преобразование, поэтому сборка слева делает неверное поле или флаг видимыми уже после первой команды, а не погребёнными в уверенном итоговом ответе. Сортировка перед подсчётом — тот шаг, который обычно пропускают: uniq схлопывает только соседние одинаковые строки, поэтому на неотсортированном вводе один эндпоинт попадает в отчёт несколько раз с мелкими счётчиками. Ловушка здесь в трёх потоках: если бы диагностика делила стандартный вывод, в подсчёт попали бы мусорные строки.
Пример 2 — Сбой, который выглядел как пустой результат
Скрипт считает сегодняшние ошибки и печатает их число. В запуске ниже появился 0, хотя файла журнала на самом деле не было. Скажите, откуда взялся нуль, и перепишите скрипт так, чтобы сбой нельзя было прочитать как достоверный результат.
count=$(grep -c "ERROR" /var/log/app.log)
echo "ошибок: $count"
# ошибок: 0 <- файла не существует
Решение
if count=$(grep -c "ERROR" /var/log/app.log); then
echo "ошибок: $count"
else
echo "не удалось прочитать журнал (код $?)" >&2
exit 1
fi
Успех команда отмечает нулевым статусом завершения, а неудачу — любым другим; grep в этом примере провалился, но всё равно записал 0 в стандартный вывод, поэтому отличить спокойный день от файла, которого нет, скрипт не мог. Неоднозначность снимает проверка статуса: путь сбоя говорит, что именно не удалось прочитать, и завершается ненулём, чтобы это заметил тот, кто вызвал скрипт. Ловушка же в том, что число выглядит совершенно достоверным, поэтому неисправность всплывает гораздо позже — как дашборд, который тихо показывал нуль.