С чего начать Первые шаги трек · 15 ступеней
Linux Labs

sed и awk — правка и поля текстового потока

9 мин Ubuntu 24.04 · GNU sed 4.9 · GNU awk 5.2

sed меняет и удаляет строки по шаблону, awk делит строку на поля и считает по ним; оба читают текст построчно.

Шпаргалка

8 команд Версия для печати
  • sed -n 's/old/new/p' app.ini проба: какие строки изменятся
  • sed -i.bak 's/old/new/g' app.ini правка, копия в app.ini.bak
  • sed -n '5,10p' file строки с 5 по 10
  • sed '/^ *#/d; /^$/d' file без комментариев и пустых строк
  • sed '/^ *#/! s/old/new/' file заменить вне комментариев
  • awk '{print $1}' access.log первое поле каждой строки
  • awk -F: '$3 >= 1000 {print $1}' /etc/passwd фильтр по полю
  • awk '{s += $4} END {print s}' log.txt сумма четвёртого поля

Обзор

sed и awk читают текст построчно — из файла или конвейера — и печатают результат. Файл остаётся прежним, пока sed не получит ключ -i.

  • sed — потоковый редактор: s/шаблон/замена/ меняет текст, d удаляет строку, p печатает её.
  • awk — делит строку на поля $1, $2… и выполняет действие для строк, которые подходят под условие.

На учебных машинах sed — GNU sed 4.9, awk — GNU awk 5.2.

Команда s

s/шаблон/замена/флаги. Шаблон — регулярное выражение, флаги: g — все совпадения в строке (без него только первое), p — напечатать изменённую строку, I — без учёта регистра. Разделителем служит любой символ после s: s#/old/path#/new/path# удобнее, когда в шаблоне есть /.

Точка в шаблоне — любой символ, поэтому адрес без \. находит лишнее:

text
student@ubuntu:~$ cat app.ini
# db_host=10.20.0.15:5432 old
db_host=10.20.0.15:5432
name=app
db_host = 10.20.0.15:5432
cache=10.20.0.150:6379
student@ubuntu:~$ sed -n 's/10.20.0.15/X/p' app.ini
# db_host=X:5432 old
db_host=X:5432
db_host = X:5432
cache=X0:6379

10.20.0.150 тоже подошёл. Шаблон 10\.20\.0\.15: с экранированными точками и двоеточием в конце совпадает только с нужным адресом.

Адрес строки

Перед командой можно указать, к каким строкам её применять:

Адрес Строки
5 пятая
5,10 с пятой по десятую
$ последняя
/^db_host/ подходящие под шаблон
/^ *#/! все, кроме подходящих

! после адреса обращает его. Так замена обходит закомментированные строки:

bash
sed -n '/^ *#/! s/10\.20\.0\.15:/10.30.0.7:/p' app.ini
text
db_host=10.30.0.7:5432
db_host = 10.30.0.7:5432

Группы и \1

С ключом -E (расширенные выражения) скобки ( ) запоминают часть совпадения, а \1, \2 вставляют её в замену:

bash
sed -E 's/^(db_host) *= *.*/\1=10.30.0.7:5432/' app.ini
text
# db_host=10.20.0.15:5432 old
db_host=10.30.0.7:5432
name=app
db_host=10.30.0.7:5432
cache=10.20.0.150:6379

Обе записи ключа, с пробелами и без, стали одинаковыми, комментарий не тронут: шаблон начинается с ^. Без -E скобки — обычные символы, и \1 ссылаться не на что (см. неполадку invalid reference).

Поля awk

Программа awk — пары условие {действие}. $0 — вся строка, $1… — поля, NF — число полей, NR — номер строки. Условие — сравнение поля ($4 == "refund"), номер строки (NR > 1 пропускает заголовок) или шаблон (/error/). Без условия действие выполняется для каждой строки, без действия подходящая строка печатается целиком.

Блок END {…} выполняется после последней строки, массив s[ключ] собирает значения по ключу:

text
student@ubuntu:~$ cat log.txt
GET /a 200 512
GET /b 404 0
POST /a 200 1024
GET /a 500 128
bash
awk '{s[$3] += $4} END {for (k in s) print k, s[k]}' log.txt | sort
text
200 1536
404 0
500 128

Порядок for (k in s) не задан, поэтому результат сортируют. Поля делятся по пробелам и табуляциям; другой разделитель задаёт -F: awk -F: … для /etc/passwd, awk -F, … для CSV без кавычек.

Ключи

5

Основные ключи обеих программ — в таблице ниже. Полный список — man sed, man gawk.

Ключ Что делает Пример
-i SUFFIX sed — править файл на месте; без аргумента не оставляет резервной копии. опасно sed -i.bak 's/old/new/g' file
-n sed — не печатать строки автоматически; используется с командой p. sed -n '5,10p' file
-F SEP awk — задать разделитель полей вместо пробелов/табов по умолчанию. awk -F: '{print $1}' /etc/passwd
-E sed — расширенные регулярные выражения (без экранирования групп). sed -E 's/(a|b)+/X/g' file
-v var=val awk — передать значение переменной внутрь программы. awk -v n=5 '{print $n}' file

Ничего не найдено. Попробуй запрос короче.

Рецепты

5

Проверить замену, потом править

bash
sed -n 's/10\.20\.0\.15:/10.30.0.7:/p' app.ini     # что изменится
sed -i.bak 's/10\.20\.0\.15:/10.30.0.7:/' app.ini  # правка с копией
diff app.ini.bak app.ini                          # что изменилось

diff покажет каждую изменённую строку до и после:

text
student@ubuntu:~$ sed -i.bak 's/^name=app$/name=web/' app.ini
student@ubuntu:~$ diff app.ini.bak app.ini
3c3
< name=app
---
> name=web

Удалить строки

bash
sed -i '/^ *#/d; /^$/d' app.ini    # комментарии и пустые строки
sed -i '/^cache=/d' app.ini        # строку с ключом

Заменить строку целиком или добавить рядом

bash
sed -i '/^name=/c\name=web' app.ini        # заменить строку
sed -i '/^name=/a\env=prod' app.ini        # добавить строку после

Одна правка во многих файлах

bash
grep -rl '10\.20\.0\.15:' /etc/app.d/      # где встречается
sed -i.bak 's/10\.20\.0\.15:/10.30.0.7:/' /etc/app.d/*.conf

Сначала список файлов, потом правка: так видно, что именно поменяется.

Фильтр и подсчёт в awk

text
student@ubuntu:~$ awk '$3 >= 500' log.txt
GET /a 500 128
student@ubuntu:~$ awk 'END {print NR}' log.txt
4
student@ubuntu:~$ awk -F: '$3 >= 1000 {print $1}' /etc/passwd
nobody
student

Неполадки

7

sed -i ничего не поменял

Шаблон не совпал ни с одной строкой. Прогони ту же замену с -n … p: пустой вывод значит, что дело в шаблоне — регистр, пробелы вокруг =, точка или * без \.

Заменилось лишнее

Шаблон шире, чем нужно: точка без \ совпала с любым символом, нет ^ в начале, нет адреса для комментариев. Сверь вывод sed -n … p до правки.

unterminated `s' command

sed 's/a/b' — не хватает последнего разделителя. Команда целиком — s/шаблон/замена/.

unknown option to `s'

В шаблоне или замене есть /, и sed принял остаток за флаги. Смени разделитель: s#/old/path#/new/path#.

invalid reference \1 on `s' command's RHS

В замене стоит \1, а группы в шаблоне нет: скобки без -E — обычные символы. Добавь -E или пиши группу как \(…\).

awk: unexpected newline or end of string

Не закрыта скобка или кавычка в программе:

text
awk: cmd. line:1: {print $1
awk: cmd. line:1:          ^ unexpected newline or end of string

Программу awk бери в одинарные кавычки — тогда $1 не подставит оболочка.

awk печатает пустые поля

Строка делится не тем разделителем: в /etc/passwd поля разделены :, в CSV — запятой. Задай -F.

Опасно

2

sed -i без пробы и копии

sed -i переписывает файл без подтверждения. Ошибка в шаблоне по многим файлам сразу (*.conf) откатывается только из копий: пиши -i.bak и проверяй замену с -n … p до правки.

Правка на месте создаёт новый файл

sed -i пишет результат во временный файл и ставит его на место прежнего. Символическая ссылка при этом превращается в обычный файл, а жёсткая ссылка перестаёт указывать на правленое содержимое. Для ссылок нужен ключ --follow-symlinks или правка цели.

См. также

  • regex — синтаксис шаблонов для s и адресов.
  • grep — найти строки и файлы до правки.
  • diff-patch — сравнить файл до и после.
  • xargs — передать список файлов в sed -i.
  • find — выбрать файлы для правки.
  • cut — простые поля без awk.
  • text-tools — sort, uniq и соседи для отчётов.
  • redirection — конвейеры, через которые идёт текст.
Загрузка…