На собесе по Linux почти всегда дают лог и просят «посчитать, найти, вытащить». Пять утилит закрывают 90% таких задач. Главная идея: каждая делает одно дело, а пайп склеивает их в конвейер.
grep: найти строки
grep ERROR app.log # строки с ERROR
grep -i error app.log # без учёта регистра
grep -v DEBUG app.log # всё, кроме DEBUG
grep -c ERROR app.log # число строк с совпадением
grep -n timeout app.log # с номерами строк
grep -r "TODO" src/ # рекурсивно по каталогу
grep -E '5[0-9]{2} ' access.log # расширенные регулярки
grep -o 'user=[a-z]*' app.log # только совпавшая часть
grep -w 'id' file # целое слово, не «idle»
grep -A2 -B1 Exception app.log # контекст: 2 строки после, 1 до
grep -F 'a.b[1]' file # фиксированная строка без регулярок
-c считает строки, а не вхождения. Чтобы посчитать все вхождения: grep -o pattern | wc -l.
sed: потоковая правка
sed 's/foo/bar/' f # первая замена в каждой строке
sed 's/foo/bar/g' f # все замены
sed -n '10,20p' f # напечатать строки 10-20
sed '/^#/d' f # удалить комментарии
sed -i.bak 's/8080/9090/' app.conf # правка файла с бэкапом
sed -E 's/([0-9]+)ms/\1 ms/' f # группы и обратные ссылки
Разделитель в s может быть любым: sed 's#/var/log#/data/log#g' удобнее для путей. -i без суффикса на macOS (BSD sed) требует пустой аргумент -i '', в GNU sed нет. Это частая причина «скрипт работал у меня на маке».
awk: колонки и подсчёты
awk читает строки, делит их на поля $1, $2, ... (по пробелам или по -F), $0 вся строка, NF число полей, NR номер строки.
awk '{print $1}' access.log # первая колонка
awk -F: '{print $1, $7}' /etc/passwd # логин и shell
awk '$9 >= 500' access.log # фильтр по колонке
awk '{s += $10} END {print s}' access.log # сумма
awk '{c[$1]++} END {for (ip in c) print c[ip], ip}' access.log # группировка
awk 'NR > 1' data.csv # пропустить заголовок
awk -v lim=100 '$2 > lim' f # переменная снаружи
Ассоциативные массивы делают из awk маленький GROUP BY. Порядок обхода for (k in arr) не определён, поэтому результат сортируют отдельно.
sort, uniq, cut, wc, tr, head, tail
sort -n # числовая сортировка (иначе 10 < 9)
sort -k2,2nr # по второму полю, числом, по убыванию
sort -t, -k3,3 # разделитель запятая
uniq -c # схлопнуть ПОДРЯД идущие повторы со счётчиком
cut -d, -f2 # второе поле CSV
tr 'a-z' 'A-Z' # заменить символы
tail -f app.log # следить за ростом файла
uniq работает только с соседними строками, поэтому перед ним почти всегда sort. Классика: топ-10 IP по запросам:
awk '{print $1}' access.log | sort | uniq -c | sort -rn | head -10
find: искать файлы
find /var/log -name '*.log' -mtime +7 # старше 7 суток
find . -type f -size +100M # больше 100 МиБ
find . -type d -empty # пустые каталоги
find /srv -user deploy -perm -o+w # владелец и права
find . -name '*.tmp' -delete # удалить найденное
find . -name '*.py' -exec grep -l TODO {} + # выполнить команду пачкой
Маску в кавычках: без них shell сам раскроет *.log в текущем каталоге. -exec ... {} \; запускает команду на каждый файл, {} + пачками, это гораздо быстрее.
xargs: из строк в аргументы
find . -name '*.log' -print0 | xargs -0 gzip
cat urls.txt | xargs -n1 -P4 curl -sO # по одному, 4 параллельно
grep -l old *.conf | xargs sed -i 's/old/new/'
xargs по умолчанию режет ввод по пробелам и переводам строк, поэтому имя my file.log превратится в два аргумента. Безопасная пара: find -print0 | xargs -0. Флаг -r (GNU) не запускает команду на пустом вводе.
Регулярные выражения
Базовые (grep, sed по умолчанию) и расширенные (grep -E, sed -E, awk). В базовых +, ?, |, () и {} надо экранировать (\+, \{3\}), в расширенных нет. ^ начало строки, $ конец, . любой символ, [0-9] класс, \b или -w граница слова.
Частые ошибки
uniq -cбезsort: одинаковые строки, идущие не подряд, посчитаются отдельно.sortдля чисел без-n.grep -cвместо подсчёта всех вхождений.xargsбез-0на именах с пробелами.find -name *.logбез кавычек.sed -iв скрипте, который запускают и на Linux, и на macOS.
Что спросят на собесе
- Найти топ IP или URL в access-логе одной строкой.
- Чем
-exec {} \;отличается от-exec {} +и отxargs. - Почему перед
uniqнуженsort. - Как заменить строку во всех конфигах каталога.
- Удалить файлы старше N дней и почему
find | xargs rmопасен без-print0.