Sed и awk: потоковый редактор и машина по колонкам
sed и awk — две команды, к которым тянется рука, когда grep уже не хватает, а писать скрипт ещё рано. Обе читают поток строка за строкой, но смотрят на строку по-разному: sed видит текст, который надо править, awk — колонки, с которыми надо считать. Ниже — обзор ключей и опций, которых хватает в подавляющем большинстве повседневных задач.
Две команды, один конвейер
sed (stream editor) — потоковый редактор, который переписывает текст построчно: файл или всё, что приходит по конвейеру.
awk — по сути небольшой язык программирования для табличных данных: он считает каждую строку записью, разбитой на поля, и умеет переменные, условия, массивы и printf.
Обе команды хорошо сочетаются со всем остальным: grep находит строки, sed их преобразует, awk извлекает и считает, sort/uniq сортируют и подсчитывают.
sed: ключи, которые реально нужны
| Ключ | Значение |
|---|---|
-n |
не печатать по умолчанию; печатать только то, что попросит команда p |
-e <script> |
добавить скрипт; повторяется, скрипты выполняются по порядку |
-E / -r |
расширенные регэкспы: +, ?, |, () без обратных слэшей |
-i[.суффикс] |
правка файла на месте; с суффиксом — сохранить бэкап |
-f <файл> |
читать скрипт из файла |
-- |
конец опций (чтобы править файл, начинающийся с -) |
Замена: s/шаблон/замена/флаги
Рабочая лошадка. Частые флаги: g (все совпадения, а не только первое), i (без учёта регистра), p (печатать строку, если замена была, — в паре с -n) и число (2), чтобы заменить только n-е совпадение. & — всё совпадение, \1…\9 — группы захвата.
sed 's/old/new/' file # первое совпадение в каждой строке
sed 's/old/new/g' file # все совпадения
sed -E 's/(.*)@(.*)/\2:\1/' file # поменять местами вокруг @
sed 's/[[:space:]]\+/_/g' file # пробелы -> подчёркивания
Печать, удаление, адреса
Перед командой можно поставить адрес: номер строки, $ (последняя строка), /регэксп/ или диапазон 2,5, /начало/,/конец/.
sed -n '10,20p' file # строки с 10 по 20
sed -n '/error/p' file # только подходящие строки
sed '/^#/d' file # выкинуть комментарии
sed 's/foo/bar/; /baz/d' file # несколько команд в одном скрипте
p печатает, d удаляет, s заменяет, q выходит досрочно, = печатает номер строки, a/i/c добавляют/вставляют/заменяют строки целиком.
Правка файла на месте
sed -i 's/8080/9090/' config.txt # переписать файл
sed -i.bak 's/demo/prod/' config.txt # оставить config.txt.bak
В macOS тот же ключ требует явный суффикс: sed -i '' 's/a/b/' file.
awk: ключи, которые реально нужны
| Ключ | Значение |
|---|---|
-F <разделитель> |
разделитель полей (по умолчанию — любые пробелы) |
-v имя=значение |
задать переменную до запуска программы |
-f <файл> |
читать программу из файла |
-- |
конец опций |
Программа — это список правил шаблон { действие }. Шаблон отбирает строки, действие выполняется над ними. BEGIN выполняется до первой строки, END — после последней.
Поля и встроенные переменные
Строка целиком — $0; $1, $2, … — поля, $NF — последнее. NF — число полей, NR — номер строки по всему вводу, FNR — номер строки в текущем файле, FS/OFS — разделители ввода/вывода.
awk '{print $1}' access.log # первая колонка
awk '{print $NF}' access.log # последняя колонка
awk -F: '{print $1, $3}' /etc/passwd # свой разделитель
awk -v limit=500 '$4 >= limit' access.log
Шаблоны и условия
awk '$4 >= 500' access.log # фильтр по полю
awk '/POST/' access.log # фильтр по регэкспу
awk 'NR > 1' file # пропустить заголовок
awk '$1 == "root", $1 == "daemon"' file # диапазон записей
BEGIN, END и арифметика
awk 'BEGIN {print "ip count"} {c[$1]++} END {for (k in c) print k, c[k]}' access.log
awk '{sum += $4} END {print "total", sum}' access.log
awk 'END {print NR " lines"}' file
Отчёты через printf
awk '{printf "%-10s %s\n", $1, $3}' access.log
%s, %d, %f, ширина и - (выравнивание влево) работают как в printf из C. Удобно превращать лог в таблицу.
Несколько рецептов на память
# суммарный размер файлов в текущем каталоге
ls -l | awk '{s += $5} END {print s}'
# уникальные IP, самые частые сверху
awk '{print $1}' access.log | sort | uniq -c | sort -rn
# сделать CSV из вывода, разделённого пробелами
awk 'BEGIN {OFS=","} {print $1, $2, $3}' data.txt
# убрать пустые строки, сжать пробелы, обрезать концы
sed -e '/^$/d' -e 's/ */ /g' -e 's/^ //; s/ $//' file
# переименовать все *.jpeg в *.jpg в текущем каталоге
for f in *.jpeg; do mv "$f" "$(echo "$f" | sed 's/\.jpeg$/.jpg/')"; done
Что выбрать
Берите sed для правки текста на уровне строк: заменить, удалить, вставить, переписать поток или файл на месте. Берите awk, когда строка — это запись из полей и нужно выбрать, посчитать, просуммировать или отформатировать. Когда не подходит ни то, ни другое, они в одном конвейере друг от друга.
У обеих есть -i и -n, которые означают не одно и то же, обе понимают расширенные регэкспы (sed -E, awk использует POSIX ERE по умолчанию), и обе есть практически в любой Unix-подобной системе — поэтому рецепты выше переносимы.
См. также
man sedиman awk- Руководство GNU sed — флаги
s///и адреса целиком - Руководство GNU awk (gawk) — весь язык