Модуль 06 — Текст: grep, sed, awk

Найти, заменить, посчитать — в журналах и конфигурациях

Прогресс курса Модуль 6 из 9

Что вы освоите в этом модуле

01

Регулярные выражения

Регулярное выражение — шаблон для поиска в тексте. Его понимают grep, sed, awk, [[ =~ ]] в bash, редакторы и языки программирования. Это не шаблоны имён файлов: там * — «что угодно», здесь — «повтор предыдущего».

ЭлементЗначениеПример
.Любой один символh.t — hat, hot
[abc], [0-9], [^#]Один символ из набора / не из набора[0-9] — цифра
*, +, ?Повтор предыдущего: 0 и больше, 1 и больше, 0 или 1[0-9]+ — число
{n,m}От n до m повторов[0-9]{1,3}
^, $Начало, конец строки^# — комментарий
(a|b)Группа, «или»(GET|POST)
\.Буквальная точка (любой спецсимвол — через \)\.php

Есть два диалекта. В базовом (BRE, по умолчанию в grep и sed) + ? { } ( ) | — обычные символы, а их особый смысл включается обратной косой чертой. В расширенном (ERE) — наоборот. Пишите расширенные: grep -E, sed -E; awk понимает их всегда.

Шаблон всегда в одинарных кавычках: grep -E '^[0-9]+$'. В двойных bash подставит $ и \ раньше grep, и шаблон станет другим.
02

grep: найти

Журнал nginx для примеров — строка на запрос: адрес, время, запрос, код ответа, размер, клиент.

192.168.1.10 - - [14/Oct/2026:10:01:12 +0300] "GET / HTTP/1.1" 200 5120 "-" "Mozilla/5.0"
10.0.0.7 - - [14/Oct/2026:10:02:03 +0300] "GET /admin HTTP/1.1" 404 162 "-" "curl/8.5.0"
192.168.1.22 - - [14/Oct/2026:11:07:10 +0300] "GET /api/orders HTTP/1.1" 500 98 "-" "Mozilla/5.0"
$ grep -E '" (4|5)[0-9]{2} ' access.log | wc -l           # ответы с ошибкой 4xx и 5xx
5
$ grep -oE '^[0-9]{1,3}(\.[0-9]{1,3}){3}' access.log | sort -u    # -o: только совпадение
10.0.0.7
192.168.1.10
192.168.1.15
192.168.1.22
$ grep -E 'POST|500' access.log
192.168.1.10 - - [14/Oct/2026:10:02:30 +0300] "POST /login HTTP/1.1" 302 0 "-" "Mozilla/5.0"
192.168.1.22 - - [14/Oct/2026:11:07:10 +0300] "GET /api/orders HTTP/1.1" 500 98 "-" "Mozilla/5.0"

Кавычка и пробелы вокруг кода в первом шаблоне — не украшение: без них [45][0-9]{2} совпадёт и с размером ответа 5120. Хороший шаблон опирается на то, что вокруг искомого.

ОпцияДействие
-qНичего не выводить, только код возврата — для if
-oВыводить только совпавшую часть
-wСовпадение целым словом
-FШаблон — обычная строка, без регулярных выражений (точки, скобки буквально)
-PРегулярные выражения Perl: \d, \K, просмотр вперёд
$ echo "id=42 user=anna" | grep -oP 'user=\K\w+'     # \K — «выводить с этого места»
anna
03

sed: заменить

sed — потоковый редактор: читает строки, применяет команды и выводит результат. Главная команда — замена s/шаблон/замена/флаги.

$ cat app.conf
# Настройки приложения
port = 8080
#debug = true
host = 127.0.0.1

log_level = info
$ sed 's/8080/9090/' app.conf | grep port
port = 9090
$ sed -E 's/^#?debug = .*/debug = false/' app.conf | grep debug    # закомментированная или нет
debug = false
$ sed -e '/^#/d' -e '/^$/d' app.conf                         # без комментариев и пустых строк
port = 8080
host = 127.0.0.1
log_level = info
ЗаписьДействие
s/a/b/, s/a/b/gЗаменить первое / все вхождения в строке
s|/var/www|/srv|gДругой разделитель — когда в шаблоне есть /
-E 's/(..)-(..)/\2-\1/'Группы и обратные ссылки \1, \2
/шаблон/dУдалить строки, где есть шаблон
-n '2,3p', -n '/шаблон/p'Вывести только строки 2–3 / только совпавшие
/шаблон/a текстДобавить строку после совпавшей (i — перед)
$ sed -E 's/([0-9]+)\.([0-9]+)\.([0-9]+)\.([0-9]+)/\4.\3.\2.\1/' <<< 'host 192.168.1.10'
host 10.1.168.192
$ sed 's|/var/www/html|/srv/site|g' <<< 'root /var/www/html; alias /var/www/html/img;'
root /srv/site; alias /srv/site/img;
04

Правка файлов: sed -i

С опцией -i sed меняет файл на месте. Суффикс после -i оставляет копию оригинала — используйте его, пока скрипт не отлажен:

$ sed -i.bak 's/^log_level = .*/log_level = warn/' app.conf
$ diff app.conf.bak app.conf
6c6
< log_level = info
---
> log_level = warn

Скрипт настройки должен быть идемпотентным: второй запуск не меняет то, что уже сделано первым. Замена через s/^параметр = .*/…/ идемпотентна. А дописывание строки — нет: два запуска дадут две строки. Поэтому сначала проверка:

grep -q '^timeout' app.conf || echo 'timeout = 30' >> app.conf

Запущенная дважды, эта строка оставит в файле ровно одну запись timeout. Для системных файлов полный порядок: копия → правка → проверка синтаксиса встроенной командой службы (sshd -t, nginx -t) → перезагрузка настроек; при неудаче — вернуть копию.

sed не понимает структуру файла. Для YAML, JSON и XML замена регулярным выражением легко ломает отступы и кавычки — там используют специальные утилиты (jq для JSON) или средства управления конфигурацией (курс «Основы Ansible»).
05

awk: поля и условия

awk делит каждую строку на поля: $1, $2, …, $NF — последнее, $0 — вся строка. Разделитель по умолчанию — любые пробелы, другой задаётся -F. Программа awk — пары условие {действие}: действие выполняется для строк, где условие истинно.

$ awk '{print $1, $9}' access.log | head -3        # адрес и код ответа
192.168.1.10 200
192.168.1.15 200
10.0.0.7 404
$ awk '$9 == 404 {print $7}' access.log            # какие пути не найдены
/admin
/wp-login.php
/.env
/phpmyadmin/
$ awk -F: '$3 >= 1000 && $3 < 65534 {print $1, $6}' /etc/passwd    # люди, не системные
student /home/student
$ awk '/Mozilla/ && $9 >= 500' access.log           # без действия — вывести строку
192.168.1.22 - - [14/Oct/2026:11:07:10 +0300] "GET /api/orders HTTP/1.1" 500 98 "-" "Mozilla/5.0"

Число полей NF, номер строки NR. Значение из bash передают через -v, а не вставкой в текст программы:

$ awk -v limit=1000 '$10 > limit {printf "%-20s %8d\n", $7, $10}' access.log
/                        5120
/about.html              2048
/                        5120
/contacts.html           1830
/img/logo.png           15400
$ df -P | awk 'NR > 1 && $5+0 >= 80 {print $6, $5}'     # разделы, заполненные на 80% и больше

$5+0 превращает «18%» в число 18: awk берёт числовое начало строки.

06

awk: суммы и группировки

Блок END выполняется после последней строки — там выводят итоги; BEGIN — до первой. Переменные создаются при первом использовании со значением 0.

$ awk '{bytes += $10} END {print bytes, NR, bytes/NR}' access.log
30264 11 2751,27
$ awk '{n[$1]++} END {for (ip in n) print n[ip], ip}' access.log | sort -rn
4 10.0.0.7
3 192.168.1.10
2 192.168.1.22
2 192.168.1.15

n[$1]++ — массив awk с ключом-адресом: так в одну строку делается то, на что в bash ушёл цикл с ассоциативным массивом. Сложнее — запросы по часам. Разделитель [][] — «квадратная скобка», второе поле — время, split режет его по двоеточию:

$ awk -F'[][]' '{split($2, t, ":"); h[t[2]]++} END {for (k in h) print k":00", h[k]}' access.log | sort
10:00 7
11:00 4
Обратите внимание на 2751,27: в русской локали awk выводит дробные числа с запятой, и следующая программа может их не понять. В скриптах, которые считают, задавайте LC_ALL=C: LC_ALL=C awk 'BEGIN {printf "%.2f\n", 10/3}' → 3.33.

awk разбирает простой текст, но не CSV с кавычками: в строке a,b,"c,d",e он насчитает 5 полей вместо 4. Для настоящего CSV и JSON берите Python или jq.

07

Какой инструмент выбрать

ЗадачаИнструментПример
Есть ли строка, отобрать строкиgrepgrep -q '^PermitRootLogin no' sshd_config
Вытащить кусок по шаблонуgrep -ogrep -oE '[0-9.]+'
Заменить в тексте или файлеsedsed -i 's/^port = .*/port = 9090/'
Поле по номеру, разделитель — пробелыawkawk '{print $9}'
Посчитать, сгруппировать, сравнить числаawkawk '{s += $10} END {print s}'
Поле по номеру, разделитель — один символcutcut -d: -f1
Заменить или удалить символыtrtr -dc 0-9, tr -s ' '

Цепочка grep | awk часто сворачивается в один awk: grep 404 | awk '{print $7}' = awk '$9 == 404 {print $7}' — и второе точнее, потому что проверяет именно поле кода, а не «404» где угодно в строке.

Ключевые выводы модуля

Запомните главное

  • Регулярные выражения — расширенные (-E), в одинарных кавычках, с опорой на окружение искомого
  • grep — найти и проверить (-q, -o, -w, -F)
  • sed — s/шаблон/замена/g; правка файла — -i.bak, затем проверка и перезагрузка службы
  • Скрипт настройки идемпотентен: замена вместо дописывания, дописывание — после grep -q
  • awk — поля, условия, суммы и группировки; -v для переменных, LC_ALL=C для чисел
Практические задания — на учебном портале. Задания, критерии оценивания, сдача работ и оценки преподавателя — в курсе на portal.nevabit.ru. Учётную запись выдаёт преподаватель.
Модуль 05: Функции Все модули Модуль 07: Надёжность