Linux sed awk CLI

Sed и awk: потоковый редактор и машина по колонкам

🇬🇧 English version

sed и awk — две команды, к которым тянется рука, когда grep уже не хватает, а писать скрипт ещё рано. Обе читают поток строка за строкой, но смотрят на строку по-разному: sed видит текст, который надо править, awk — колонки, с которыми надо считать. Ниже — обзор ключей и опций, которых хватает в подавляющем большинстве повседневных задач.

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-подобной системе — поэтому рецепты выше переносимы.

См. также