Перейти к содержанию

sed

Команда SED — Полное руководство по потоковому редактору в Linux

Что такое SED?

SED (Stream EDitor) — консольная утилита для редактирования текстовых потоков. Разработана для Unix, широко портирована на все платформы. Название отражает суть: SED читает поток текста построчно и применяет к каждой строке заданную микропрограмму.

Принцип работы:

Входной поток → [pattern space] → применение команд → STDOUT
                 [hold space]  ← дополнительный буфер

Типичные задачи: - Замена подстрок по регулярным выражениям - Удаление, вставка, изменение строк - Вывод фрагментов файла - Автоматизированная правка конфигурационных файлов - Построение простых текстовых препроцессоров

💡 Правило выбора инструмента: сначала попробуй решить задачу через SED, и только если он не справляется — переходи к AWK.


Синтаксис

sed [опции] 'микропрограмма' файл1 файл2 ...
sed [опции] -f script.sed файл

Способы передачи данных

# Через конвейер — самый распространённый способ
echo "Text fragment" | sed 's/fragment/example/'
# → Text example

# Через heredoc — удобно для многострочного ввода
sed 's/old/new/' <<EOF
old line 1
old line 2
old line 3
EOF
# → new line 1
# → new line 2
# → new line 3

# Через herestring — для одной строки
sed 's/Hello/Привет/' <<< "Hello World"
# → Привет World

# Через файл — два эквивалентных варианта
sed 's/old/new/' < file.txt   # через перенаправление
sed 's/old/new/' file.txt     # файл как аргумент

# Process Substitution (Bash) — вывод команды как файл
sed 's/root/ROOT/' < <(grep "root" /etc/passwd)

# Обработка нескольких файлов — по умолчанию конкатенируются
sed 's/old/new/' file1.txt file2.txt file3.txt

# Обработать каждый файл ОТДЕЛЬНО (не конкатенировать)
# Полезно когда нужно считать строки в каждом файле независимо
sed -s 's/old/new/' file1.txt file2.txt

# НЕЛЬЗЯ: конвейер игнорируется при herestring
echo "text" | sed 'p' <<< "other"   # ❌ данные из echo проигнорируются

Как SED обрабатывает строки

SED обрабатывает каждую строку в три шага:

┌─────────────────────────────────────────────────────┐
│  1. Прочитать строку → поместить в pattern space    │
│  2. Применить все команды микропрограммы            │
│  3. Вывести pattern space в STDOUT (авто-печать)    │
└─────────────────────────────────────────────────────┘

⚠️ Важно про автоматическую печать: SED всегда выводит содержимое pattern space в конце каждого цикла — если не указана опция -n. Команда p добавляет дополнительный вывод, что приводит к дублированию.

# Проблема дублирования
sed 'p' <<< "Hello World"
# Hello World   ← вывод команды p
# Hello World   ← автоматическая печать в конце цикла

# Решение: подавить автоматическую печать опцией -n
sed -n 'p' <<< "Hello World"
# Hello World   ← только вывод команды p

# Практический пример: вывести только строки с "error"
# БЕЗ -n — каждая строка выведется, строки с "error" — дважды
sed '/error/p' log.txt       # ❌ дублирование

# С -n — выведутся только строки с "error"
sed -n '/error/p' log.txt    # ✅ правильно

# Команда замены s — работает с автоматической печатью
# (сама по себе НЕ печатает, только изменяет pattern space)
echo "Today is DATE" | sed 's/DATE/Monday/'
# → Today is Monday   ← одна строка, всё правильно

# Но с модификатором p нужна опция -n
echo "Today is DATE" | sed -n 's/DATE/Monday/p'
# → Today is Monday   ← напечатает только если замена была

Флаги и опции

Флаг Описание
-n / --silent Подавить автоматическую печать
-e 'команда' Передать команду напрямую (можно несколько)
-f файл Читать микропрограмму из файла
-i[суффикс] Редактировать файл на месте (in-place)
-i.bak Редактировать на месте с созданием резервной копии .bak
-E / -r Использовать ERE (расширенные регулярные выражения)
-s / --separate Обрабатывать каждый файл отдельно
-b / --binary Бинарный режим (для файлов с CRLF окончаниями строк)
# -n — подавить автоматическую печать
# Без -n: выводятся все строки, строка с "error" — дважды
sed '/error/p' log.txt

# С -n: выводятся только строки с "error"
sed -n '/error/p' log.txt

# -e — несколько команд в одной строке
# Каждая -e добавляет команду в микропрограмму
sed -e 's/foo/bar/' -e 's/baz/qux/' -e '/^$/d' file.txt
# Эквивалентно:
sed 's/foo/bar/;s/baz/qux/;/^$/d' file.txt

# -f — микропрограмма из файла
# Удобно для сложных скриптов или повторного использования
cat > myrules.sed << 'EOF'
s/foo/bar/g
s/baz/qux/g
/^$/d
EOF
sed -f myrules.sed file.txt

# -i — редактирование файла на месте
# ВНИМАНИЕ: оригинал будет перезаписан без возможности восстановления!
echo "old value" > test.txt
sed -i 's/old/new/' test.txt
cat test.txt   # → new value

# -i.bak — с созданием резервной копии
sed -i.bak 's/old/new/' test.txt
# Создаёт: test.txt (изменённый) и test.txt.bak (оригинал)
ls test.txt*   # test.txt  test.txt.bak

# -E — расширенные регулярные выражения (ERE)
# В ERE не нужно экранировать скобки и квантификаторы
sed -E 's/(foo|bar)/baz/g' file.txt   # ERE: скобки без экранирования
sed 's/\(foo\|bar\)/baz/g' file.txt   # BRE: то же самое, но с экранированием

# -s — обработка файлов по отдельности
# Без -s: $ означает конец последнего файла
sed -n '$p' file1.txt file2.txt    # выведет последнюю строку file2.txt

# С -s: $ означает конец каждого файла отдельно
sed -sn '$p' file1.txt file2.txt   # выведет последнюю строку каждого файла

# -b — бинарный режим для Windows-файлов (CRLF → LF)
sed -b 's/\r//' windows_file.txt   # удалить символы \r

Структура микропрограммы

[адрес или диапазон] команда [аргументы]
[адрес или диапазон] { команда1 ; команда2 ; ... }
# Одна команда без адреса — применяется ко ВСЕМ строкам
sed 's/a/A/' file.txt

# Команда с адресом — применяется только к строке 2
sed '2s/a/A/' file.txt

# Команда с диапазоном — строки со 2 по 5
sed '2,5s/a/A/' file.txt

# Блок команд для одного адреса
# Всё что в {} выполняется только для строки 1
sed '1 {
    s/a/A/
    s/b/B/
    s/c/C/
}' file.txt

# Вложенные блоки
sed '1,10 {
    /pattern/ {
        s/old/new/
        s/foo/bar/
    }
}' file.txt

# ВАЖНО: адрес прикрепляется только к СЛЕДУЮЩЕЙ команде/блоку
# Здесь '1' относится только к первому p, второй p — для всех строк
sed -n '1 p ; p' file.txt
# Строка 1 напечатается дважды (p + p), остальные — один раз (второй p)

# Правильно — если нужно применить несколько команд к одному адресу:
sed -n '1 { p ; p }' file.txt   # первая строка напечатается дважды

# Точка с запятой разделяет команды в однострочной записи
sed -n 'p;p;p' <<< "test"   # напечатает "test" трижды

# Перевод строки — разделитель в многострочной записи
sed -n '
    p
    p
    p
' <<< "test"

# Комментарии в многострочных микропрограммах
sed -n '
    # Напечатать строки с "error"
    /error/ p
    # Напечатать строки с "warning"
    /warning/ p
' log.txt

Адресация строк

Числовая адресация

# Файл text.txt:
# строка 1
# строка 2
# строка 3
# строка 4
# строка 5

# Конкретная строка — напечатать вторую строку
sed -n '2p' text.txt
# строка 2

# Диапазон строк (с 2 по 4 включительно)
sed -n '2,4p' text.txt
# строка 2
# строка 3
# строка 4

# Специальный символ $ — последняя строка
sed -n '$p' text.txt
# строка 5

# С третьей строки до конца файла
sed -n '3,$p' text.txt
# строка 3
# строка 4
# строка 5

# Только первая строка — часто используется для заголовков
sed -n '1p' text.txt
# строка 1

# Все строки КРОМЕ первой (пропустить заголовок)
sed '1d' text.txt
# строка 2
# строка 3
# строка 4
# строка 5

# Заменить только во второй строке
sed '2s/строка/line/' text.txt
# строка 1
# line 2
# строка 3
# строка 4
# строка 5

# Удалить строки с 2 по 4
sed '2,4d' text.txt
# строка 1
# строка 5

Адресация по регулярному выражению

# Файл с маркерами:
# строка 1
# start
# строка 3
# строка 4
# end
# строка 6

# Строки, содержащие "start"
sed -n '/start/p' text.txt
# start

# Диапазон от "start" до "end" включительно
# AWK начнёт вывод со строки "start" и закончит на "end"
sed -n '/start/,/end/p' text.txt
# start
# строка 3
# строка 4
# end

# Комбинирование: от "start" до последней строки
sed -n '/start/,$p' text.txt
# start
# строка 3
# строка 4
# end
# строка 6

# Комбинирование: с первой строки до "end"
sed -n '1,/end/p' text.txt
# строка 1
# start
# строка 3
# строка 4
# end

# По регулярному выражению — строки с цифрами
sed -n '/[0-9]/p' text.txt

# Замена только в строках с "pattern"
sed '/pattern/s/old/new/' file.txt
# Объяснение: сначала выбираем строки с "pattern", потом в них делаем замену

# Удаление строк по шаблону
sed '/^#/d' config.txt    # удалить комментарии (строки начинающиеся с #)
sed '/^$/d' file.txt      # удалить пустые строки
sed '/ERROR/d' log.txt    # удалить строки с "ERROR"

Продвинутая адресация (только GNU SED)

# строка~шаг — каждую N-ю строку начиная с M-й
# Синтаксис: первая~шаг

# Нечётные строки (начиная с 1-й, каждая вторая)
seq 8 | sed -n '1~2p'
# 1 3 5 7

# Чётные строки (начиная со 2-й, каждая вторая)
seq 8 | sed -n '2~2p'
# 2 4 6 8

# Каждая третья строка, начиная с 3-й
seq 9 | sed -n '3~3p'
# 3 6 9

# Практический пример: удалить каждую вторую строку
seq 6 | sed '2~2d'
# 1 3 5

# адрес,+N — адресованная строка и ещё N строк после неё
# Вывести строку 3 и ещё 2 после неё (строки 3, 4, 5)
seq 8 | sed -n '3,+2p'
# 3 4 5

# С regex: строка с "start" и ещё 3 строки после неё
sed -n '/start/,+3p' file.txt

# адрес,~N — от адресованной строки до ближайшей, кратной N
# С первой строки до ближайшей, кратной 5 (т.е. до строки 5)
seq 8 | sed -n '1,~5p'
# 1 2 3 4 5

# С третьей строки до ближайшей, кратной 4 (т.е. до строки 4)
seq 8 | sed -n '3,~4p'
# 3 4

Инверсия адресации — оператор !

# ! инвертирует адрес — команда выполняется для всех строк КРОМЕ указанных

# Всё КРОМЕ второй строки
sed -n '2 !p' file.txt

# Всё КРОМЕ строк со 2 по 4
sed -n '2,4 !p' file.txt
# Объяснение: строки 1, 5, 6, ... выводятся; строки 2, 3, 4 — нет

# Всё КРОМЕ строк с "pattern"
sed -n '/pattern/ !p' file.txt
# Эквивалентно: sed -n '/pattern/!p' file.txt (пробел необязателен)

# Удалить всё КРОМЕ строк с "keep"
sed '/keep/ !d' file.txt

# Обработать все файлы кроме первого — трюк с FNR/NR
# (в SED нет FNR, но есть -s для раздельной обработки)

# Инверсия с блоком — выполнить блок для всего кроме строк 2-4
sed -n '2,4 !{
    s/a/A/
    p
}' file.txt

# Практический пример: напечатать всё кроме комментариев и пустых строк
sed -n '/^#/ !{ /^$/ !p }' config.txt
# Или короче:
sed '/^#/d;/^$/d' config.txt

Разделители

По умолчанию разделитель команды s и y — слеш /. Если в шаблоне много слешей, можно использовать любой другой символ.

# Проблема: замена пути — много экранирования, плохо читается
sed 's/\/usr\/local\/bin\/old/\/usr\/local\/bin\/new/' file.txt

# Решение: сменить разделитель на |
sed 's|/usr/local/bin/old|/usr/local/bin/new|' file.txt

# Разделитель # — популярен для путей
sed 's#/usr/local/bin/old#/usr/local/bin/new#' file.txt

# Разделитель _ — если нет подчёркиваний в шаблоне
sed 's_/usr/local/bin/old_/usr/local/bin/new_' file.txt

# Разделитель @ — редко встречается в текстах
sed 's@/usr/local/bin@/opt/bin@g' file.txt

# Разделитель : — для команды y (трансформация символов)
# Перевести в нижний регистр
sed 'y:ABCDEFGHIJKLMNOPQRSTUVWXYZ:abcdefghijklmnopqrstuvwxyz:' <<< "RED ZONE"
# → red zone

# Разделитель в адресации по regex
# ВАЖНО: первый символ разделителя в адресации экранируется \
sed -n '\|/usr/local|p' /etc/paths    # разделитель |
sed -n '\#/usr/local#p' /etc/paths    # разделитель #

# В диапазоне — для каждой границы можно свой разделитель
sed -n '\|start|,\#stop#p' file.txt

# Когда разделитель встречается в шаблоне — экранировать
# Если шаблон содержит | и разделитель тоже |
sed 's|foo\|bar|baz|g' file.txt   # экранируем | в шаблоне

# Практический пример: обновить URL в конфиге
sed 's|http://old-server.com|https://new-server.com|g' config.txt

# Практический пример: замена путей в скрипте
sed 's|/home/user/project|/opt/myapp|g' deploy.sh

Команда s — замена (подробно)

Самая используемая команда SED.

s<d>шаблон<d>замена<d>модификаторы

где <d> — разделитель (обычно /).

Модификаторы команды s

Модификатор Описание
g Заменить все вхождения (по умолчанию только первое)
N (число) Заменить только N-е вхождение
i / I Игнорировать регистр
p Напечатать результат (используется с -n)
w файл Записать результат в файл
# ==========================================
# Базовая замена — первое вхождение
# ==========================================

echo "This is an old text. Replace the old text." > sample.txt

# Заменить только ПЕРВОЕ вхождение "old" на "new"
sed 's/old/new/' sample.txt
# → This is an new text. Replace the old text.
# Обратите внимание: второе "old" НЕ заменилось!

# ==========================================
# Флаг g — заменить ВСЕ вхождения
# ==========================================

# Заменить ВСЕ вхождения "old" на "new"
sed 's/old/new/g' sample.txt
# → This is an new text. Replace the new text.

# Удалить все пробелы в строке
sed 's/ //g' <<< "hello world foo bar"
# → helloworldfoobar

# Удалить все цифры
sed 's/[0-9]//g' <<< "abc123def456"
# → abcdef

# ==========================================
# Числовой модификатор — N-е вхождение
# ==========================================

echo "one two three one two three" > nums.txt

# Заменить только ВТОРОЕ вхождение "one"
sed 's/one/ONE/2' nums.txt
# → one two three ONE two three
# Первое "one" не тронуто, второе заменено

# Заменить только ТРЕТЬЕ вхождение
echo "a a a a a" | sed 's/a/X/3'
# → a a X a a

# Заменить начиная со второго вхождения (комбинация 2 и g)
echo "a a a a a" | sed 's/a/X/2g'
# → a X X X X

# ==========================================
# Флаг i — игнорировать регистр
# ==========================================

# Заменить "error" независимо от регистра
echo "Error ERROR error ErRoR" | sed 's/error/ОШИБКА/gi'
# → ОШИБКА ОШИБКА ОШИБКА ОШИБКА

# Найти строки с "warning" в любом регистре и заменить
sed 's/WARNING/⚠️ WARNING/i' log.txt

# ==========================================
# Флаг p — напечатать только изменённые строки
# ==========================================

# Без -n — напечатает все строки, изменённые — дважды
sed 's/old/new/p' file.txt    # ❌ дублирование

# С -n — напечатает только строки где произошла замена
sed -n 's/old/new/p' file.txt    # ✅ только изменённые

# Практический пример: показать только изменённые строки конфига
sed -n 's/^#\(PasswordAuthentication\)/\1/p' /etc/ssh/sshd_config

# ==========================================
# Флаг w — записать результат в файл
# ==========================================

# Сохранить строки с заменой в отдельный файл
sed 's/ERROR/ОШИБКА/w errors_ru.log' app.log
# В errors_ru.log попадут только строки где была замена

# ==========================================
# Символы в строке замены
# ==========================================

# & — подставляет ВСЁ найденное совпадение целиком
echo "Price: 100" | sed 's/[0-9]*/[&]/'
# → Price: [100]
# [0-9]* нашло "100", & подставило "100", обернули в []

# Обернуть любое слово в кавычки
echo "hello world" | sed 's/[[:alpha:]]*/"&"/g'
# → "hello" "world"

# Добавить http:// перед доменом
echo "example.com" | sed 's/.*/http:\/\/&/'
# → http://example.com
# Лучше со сменой разделителя:
echo "example.com" | sed 's|.*|http://&|'
# → http://example.com

# \1, \2, ... — ссылки на группы захвата
# Группы в BRE: \(...\), в ERE: (...)

# Вывести только первое слово строки (BRE)
echo "Hello World Foo" | sed 's/\([[:alnum:]]*\).*/\1/'
# → Hello
# \([[:alnum:]]*\) — группа 1: захватывает слово
# .*               — остаток строки (не в группе)
# \1               — подставляем группу 1

# Поменять местами имя и фамилию (ERE)
echo "Иванов Иван" | sed -E 's/([^ ]+) ([^ ]+)/\2 \1/'
# → Иван Иванов
# ([^ ]+) — группа 1: всё до пробела (фамилия)
# ([^ ]+) — группа 2: всё после пробела (имя)
# \2 \1   — имя + пробел + фамилия

# Добавить скобки вокруг кода города в телефоне
echo "Phone: 495-123-45-67" | sed -E 's/([0-9]{3})-([0-9]{3})/(\1)-\2/'
# → Phone: (495)-123-45-67

# Форматировать дату из YYYYMMDD в YYYY-MM-DD
echo "20231225" | sed -E 's/([0-9]{4})([0-9]{2})([0-9]{2})/\1-\2-\3/'
# → 2023-12-25

# ==========================================
# Замена на конкретной строке
# ==========================================

# Заменить только в первой строке
sed '1s/old/new/' file.txt

# Заменить только в последней строке
sed '$s/old/new/' file.txt

# Заменить в строках с 2 по 5
sed '2,5s/old/new/g' file.txt

# Заменить только в строках с "pattern"
sed '/pattern/s/old/new/' file.txt

# Заменить во всех строках КРОМЕ первой
sed '1 !s/old/new/' file.txt

# Практический пример: изменить значение параметра в конфиге
sed 's/^timeout=.*/timeout=30/' config.ini
# ^ — начало строки, чтобы не зацепить inline комментарии
# .* — всё что идёт после =

# ==========================================
# Практические примеры замены
# ==========================================

# Убрать ведущие пробелы (ltrim)
echo "   hello world" | sed 's/^[[:space:]]*//'
# → hello world
# ^ — начало строки
# [[:space:]]* — ноль или более пробельных символов

# Убрать хвостовые пробелы (rtrim)
echo "hello world   " | sed 's/[[:space:]]*$//'
# → hello world
# [[:space:]]* — ноль или более пробельных символов
# $ — конец строки

# Убрать ВСЕ пробелы по краям (trim) — два вызова s
echo "   hello world   " | sed 's/^[[:space:]]*//;s/[[:space:]]*$//'
# → hello world

# Удалить комментарии (от # до конца строки)
echo "param=value  # это комментарий" | sed 's/#.*//'
# → param=value

# Удалить комментарии И хвостовые пробелы после них
echo "param=value  # комментарий" | sed 's/[[:space:]]*#.*//'
# → param=value

# Заменить множественные пробелы одним
echo "hello    world   foo" | sed 's/[[:space:]][[:space:]]*/  /g'
# → hello world foo
# [[:space:]] — один пробельный символ
# [[:space:]]* — ноль или более дополнительных
# Вместе: один или более пробелов → один пробел

# Удалить ANSI escape-коды (цвета в терминале)
sed 's/\x1b\[[0-9;]*m//g' colored_output.txt

# Удалить Windows CRLF окончания строк (\r\n → \n)
sed 's/\r//' windows_file.txt
# или для редактирования на месте:
sed -i 's/\r$//' windows_file.txt

# Обновить версию в package.json
sed -i 's/"version": "[^"]*"/"version": "2.1.0"/' package.json
# "[^"]*" — любые символы кроме кавычки (содержимое строки)

# Экранировать специальные символы HTML
echo "<b>Hello & World</b>" | sed '
    s/&/\&amp;/g
    s/</\&lt;/g
    s/>/\&gt;/g
'
# → &lt;b&gt;Hello &amp; World&lt;/b&gt;
# ВАЖНО: & нужно заменять первым, иначе & из \&amp; тоже заменится

# Добавить текущую дату вместо токена DATE
sed "s/DATE/$(date +%Y-%m-%d)/" template.txt
# Двойные кавычки нужны для работы подстановки $()

Команда y — посимвольная трансформация

Аналог утилиты tr. Заменяет каждый символ из первой серии соответствующим символом из второй.

# Синтаксис: y/исходная_серия/целевая_серия/
# Длина обеих серий должна быть ОДИНАКОВОЙ!

# Перевести все буквы в нижний регистр
sed 'y/ABCDEFGHIJKLMNOPQRSTUVWXYZ/abcdefghijklmnopqrstuvwxyz/' <<< "HELLO WORLD"
# → hello world

# Перевести в верхний регистр
sed 'y/abcdefghijklmnopqrstuvwxyz/ABCDEFGHIJKLMNOPQRSTUVWXYZ/' <<< "hello world"
# → HELLO WORLD

# Заменить цифры символами (шифрование)
echo "12345" | sed 'y/0123456789/abcdefghij/'
# → bcdef
# 1→b, 2→c, 3→d, 4→e, 5→f

# Заменить точки запятыми (например, для числового формата)
echo "3.14159" | sed 'y/./,/'
# → 3,14159

# ROT13 — простое шифрование (буква смещается на 13 позиций)
echo "Hello World" | sed 'y/ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz/NOPQRSTUVWXYZABCDEFGHIJKLMnopqrstuvwxyzabcdefghijklm/'
# → Uryyb Jbeyq

# Заменить пробелы подчёркиваниями (для имён файлов)
echo "my file name.txt" | sed 'y/ /_/'
# → my_file_name.txt

# С другим разделителем (удобно если в серии есть /)
echo "/usr/local/bin" | sed 'y|/|_|'
# → _usr_local_bin

# Разделитель : — удобен для кириллицы и спецсимволов
sed 'y:+-*/:abcd:' <<< "1+2-3*4/5"
# → 1a2b3c4d5

# Отличие от s — y применяется к КАЖДОМУ символу отдельно
# s/abc/xyz/ — заменяет подстроку "abc" целиком
# y/abc/xyz/ — заменяет a→x, b→y, c→z по всей строке
echo "abcabc" | sed 's/abc/xyz/'    # → xyzabc (только первое вхождение)
echo "abcabc" | sed 'y/abc/xyz/'    # → xyzxyz (каждый символ по всей строке)

Вставка, добавление, удаление строк (подробно)

Команда a — добавить строку ПОСЛЕ

# Добавить строку после КАЖДОЙ строки
sed 'a ---' file.txt
# Вывод:
# строка 1
# ---
# строка 2
# ---

# Добавить после второй строки
sed '2a Это добавленная строка' file.txt
# строка 1
# строка 2
# Это добавленная строка
# строка 3

# Добавить после последней строки (в конец файла)
sed '$a # Конец файла' config.txt

# Добавить после строки с "pattern"
sed '/\[database\]/a host=localhost' config.ini
# Найдёт строку [database] и добавит host=localhost после неё

# Добавить несколько строк (\ в конце каждой)
sed '/\[database\]/a\
host=localhost\
port=5432\
name=mydb' config.ini

# Практический пример: добавить заголовок лицензии в файлы
find . -name "*.py" | xargs sed -i '1a # Licensed under MIT License'

# Добавить пустую строку после каждого абзаца (строки с .)
sed '/\.$/ a\\' text.txt

Команда i — вставить строку ПЕРЕД

# Вставить перед второй строкой
sed '2i Это вставленная строка' file.txt
# строка 1
# Это вставленная строка
# строка 2
# строка 3

# Вставить в начало файла (перед первой строкой)
sed '1i #!/bin/bash' script.sh
# Добавляет shebang в начало

# Вставить заголовок перед первой строкой
sed '1i # Автогенерированный файл — не редактировать вручную!' config.txt

# Вставить перед строкой с "pattern"
sed '/^server {/i # Конфигурация сервера' nginx.conf

# Практический пример: добавить DOCTYPE перед <html>
sed '/<html>/i <!DOCTYPE html>' template.html

# Вставить несколько строк
sed '/^server {/i\
# Конфигурация сервера\
# Создано автоматически' nginx.conf

Команда c — заменить строку целиком

# Заменить вторую строку целиком
sed '2c Это новое содержимое строки 2' file.txt
# строка 1
# Это новое содержимое строки 2
# строка 3

# Заменить строку с "old_param"
sed '/^old_param=/c new_param=new_value' config.txt

# ВАЖНО: при замене диапазона — весь диапазон заменяется ОДНОЙ строкой
sed '2,4c Эта строка заменяет строки 2, 3 и 4' file.txt
# строка 1
# Эта строка заменяет строки 2, 3 и 4
# строка 5

# Практический пример: заменить строку с устаревшим параметром
sed '/^PermitRootLogin yes/c PermitRootLogin no' /etc/ssh/sshd_config

# Заменить строку на несколько строк
sed '/pattern/c\
Первая строка замены\
Вторая строка замены' file.txt

Команда d — удалить строку

# Удалить вторую строку
sed '2d' file.txt

# Удалить с 3 по 5 строку
sed '3,5d' file.txt

# Удалить последнюю строку
sed '$d' file.txt

# Удалить строки с "pattern"
sed '/ERROR/d' log.txt

# Удалить пустые строки
sed '/^$/d' file.txt
# ^ — начало строки
# $ — конец строки
# Если между ними ничего нет — строка пустая

# Удалить строки состоящие ТОЛЬКО из пробелов/табов
sed '/^[[:space:]]*$/d' file.txt

# Удалить строки-комментарии (начинающиеся с #)
sed '/^[[:space:]]*#/d' config.txt
# [[:space:]]* — возможные пробелы в начале
# # — символ комментария

# Удалить комментарии И пустые строки одной командой
sed '/^[[:space:]]*#/d;/^[[:space:]]*$/d' config.txt

# Удалить строки короче 5 символов
sed '/.\{5\}/!d' file.txt
# .\{5\} — любые 5 символов
# !d — удалить строки где это НЕ нашлось (т.е. строки короче 5)

# Удалить повторяющиеся подряд идущие строки (аналог uniq)
sed '$!N;/^\(.*\)\n\1$/!P;D' file.txt
# Объяснение:
# $!N              — если не последняя строка, добавить следующую
# /^\(.*\)\n\1$/!P — если текущая != следующей, напечатать текущую
# D                — удалить из pattern space до \n, начать новый цикл

# Практический пример: удалить строки из диапазона
sed '/^BEGIN$/,/^END$/d' file.txt
# Удалит строки от BEGIN до END включительно

Команда p — вывод строк

# Базовый вывод — с -n чтобы не было дублирования
sed -n '5p' file.txt          # строка 5
sed -n '5,10p' file.txt       # строки 5-10
sed -n '/error/p' file.txt    # строки с "error"
sed -n '$p' file.txt          # последняя строка

# Вывести первые 5 строк (как head -5)
sed -n '1,5p' file.txt
# Или через удаление (быстрее для больших файлов):
sed '6,$d' file.txt

# Вывести последние 3 строки (как tail -3)
sed -n -e :a -e '$p;N;3,$D;ba' file.txt
# Объяснение сложной конструкции:
# :a    — метка начала цикла
# $p    — если последняя строка — напечатать pattern space
# N     — добавить следующую строку к pattern space
# 3,$D  — если в pattern space 3+ строк — удалить первую
# ba    — перейти к метке :a

# Вывести диапазон по шаблонам
sed -n '/^START/,/^END/p' file.txt

# Вывести строку ДО строки с "pattern" (предыдущая строка)
sed -n '/pattern/{x;p;d};x' file.txt
# Объяснение:
# /pattern/{ — если строка содержит "pattern":
#   x — обменять pattern space и hold space
#   p — напечатать (теперь в pattern space предыдущая строка)
#   d — удалить (не выводить текущую строку)
# }
# x — для остальных строк: сохранить в hold space

# Вывести строку ПОСЛЕ строки с "pattern"
sed -n '/pattern/{n;p}' file.txt
# n — перейти к следующей строке
# p — напечатать её

# Вывести строки с номерами (как cat -n)
sed -n '=' file.txt | sed 'N;s/\n/\t/'
# = выводит номер строки, затем мы объединяем с самой строкой

Команда = — номера строк

# Вывести номера всех строк ПЕРЕД каждой строкой
sed '=' file.txt
# 1
# первая строка
# 2
# вторая строка

# Вывести только количество строк (номер последней = количество)
sed -n '$=' file.txt
# Это быстрее чем wc -l для простых случаев

# Вывести номер строки с "pattern"
sed -n '/pattern/=' file.txt

# Красивый вывод: номер строки + строка
sed '=' file.txt | sed 'N;s/\n/: /'
# 1: первая строка
# 2: вторая строка

# Вывести номера строк с форматированием (ведущие нули)
sed -n '=' file.txt | sed 's/^/     /;s/.*\(.\{5\}\)/\1/'
# 00001
# 00002

Команды r и w — чтение и запись файлов

# r — прочитать файл и вставить его содержимое

# Вставить содержимое файла ПОСЛЕ строки 3
sed '3r header.txt' main.txt

# Вставить файл после строки с "INSERT_HERE"
sed '/INSERT_HERE/r insert.txt' template.txt

# Вставить файл после последней строки (добавить в конец)
sed '$r footer.txt' main.txt

# Практический пример: шаблон с заменяемыми блоками
sed '/LICENSE_PLACEHOLDER/r license.txt' template.txt | \
    sed '/LICENSE_PLACEHOLDER/d'
# Сначала вставляем файл после строки-плейсхолдера,
# потом удаляем саму строку-плейсхолдер

# w — записать строку в файл

# Записать строки с "error" в отдельный файл
sed -n '/error/w errors.log' app.log

# Записать строки 5-10 в файл
sed -n '5,10w output.txt' file.txt

# Разбить файл на части по шаблону
sed -n '/^[A-M]/w am.txt' names.txt    # имена А-М
sed -n '/^[N-Z]/w nz.txt' names.txt    # имена Н-Я

# Записать результат замены в файл
sed -n 's/ERROR/КРИТИЧЕСКАЯ ОШИБКА/pw critical.log' app.log
# p — напечатать, w — записать в файл

Работа с буферами (pattern space и hold space)

SED имеет два буфера: - pattern space — рабочий буфер, текущая обрабатываемая строка - hold space — вспомогательный буфер, пустой по умолчанию, сохраняется между строками

Команды работы с буферами:
  h  — скопировать pattern space → hold space (перезапись)
  H  — добавить pattern space к hold space (через \n)
  g  — скопировать hold space → pattern space (перезапись)
  G  — добавить hold space к pattern space (через \n)
  x  — поменять местами pattern space и hold space
# ==========================================
# G — добавить hold space к pattern space
# По умолчанию hold space пустой → добавляется пустая строка
# ==========================================

# Добавить пустую строку после каждой строки (двойной интервал)
sed 'G' file.txt
# Вывод:
# строка 1
#               ← пустая строка из hold space
# строка 2
#               ← пустая строка из hold space

# Убрать двойные пустые строки
sed 'n;/^$/d' file.txt
# n — перейти к следующей строке (пропустить чётные)
# /^$/d — удалить пустые строки

# ==========================================
# h и g — сохранить и восстановить строку
# ==========================================

# Дублировать первую строку в конце файла
sed -n '1h;1!p;${g;p}' file.txt
# 1h    — сохранить первую строку в hold space
# 1!p   — печатать все строки кроме первой
# ${    — в конце файла:
#   g   — загрузить hold space (первая строка)
#   p   — напечатать её
# }

# Вывести строку перед строкой с "MARKER"
sed -n '/MARKER/{x;p;d};x' file.txt
# Для строк без MARKER: x сохраняет текущую в hold space
# Для строки с MARKER: x загружает предыдущую строку, p печатает её

# ==========================================
# Вывод строк в обратном порядке (аналог tac)
# ==========================================

sed -n '1!G;h;$p' file.txt
# Объяснение:
# 1!G — для всех строк кроме первой: добавить hold space к pattern space
#       (в hold space накапливаются предыдущие строки в обратном порядке)
# h   — сохранить накопленное обратно в hold space
# $p  — в конце файла напечатать всё накопленное

# Проследим для файла с 3 строками:
# Строка 1: 1!G не срабатывает; h сохраняет "1" в hold
# Строка 2: G добавляет "1" → pattern="2\n1"; h сохраняет "2\n1"
# Строка 3: G добавляет "2\n1" → pattern="3\n2\n1"; $p печатает "3\n2\n1"

# ==========================================
# Объединение подряд идущих строк попарно (GNU SED)
# ==========================================

printf "%s\n" a b c d e f | sed -n '1~2h; 2~2 { H; g; s/\n/ /; p }'
# → a b
# → c d
# → e f
# Объяснение:
# 1~2h      — нечётные строки (1,3,5...): сохранить в hold space
# 2~2 {     — чётные строки (2,4,6...):
#   H       — добавить к hold space (получаем "нечётная\nчётная")
#   g       — загрузить в pattern space
#   s/\n/ / — заменить \n на пробел
#   p       — напечатать пару
# }

# ==========================================
# Захват абзацев (блоков разделённых пустыми строками)
# ==========================================

sed '/./{H;$!d} ; x ; s/^/\n>>> АБЗАЦ:\n/' text.txt
# Объяснение:
# /./ {     — для непустых строк:
#   H       — добавить к hold space
#   $!d     — удалить из вывода (если не последняя строка)
# }
# x         — когда встречаем пустую строку или EOF:
#             обменять буферы (получаем накопленный абзац)
# s/^/.../  — добавить маркер начала абзаца

# ==========================================
# x — обмен буферами
# ==========================================

# Вывести только чётные строки в обратном порядке
sed -n 'h;n;x;p' file.txt
# h — сохранить нечётную строку в hold
# n — перейти к чётной строке
# x — обменять (в pattern space нечётная, в hold чётная)
# p — напечатать чётную

# Вставить строку-разделитель между каждыми двумя строками
sed 'x;/./p;x' file.txt
# Первый x: при первой строке hold space пуст → p не выводит ничего
# При второй строке: x загружает первую, p печатает разделитель если hold непустой

Команды n и N — работа с несколькими строками

# n — прочитать СЛЕДУЮЩУЮ строку в pattern space (ЗАМЕНЯЕТ текущую)
# N — ДОБАВИТЬ следующую строку к pattern space (через \n)

# ==========================================
# n — пропустить строку (перейти к следующей)
# ==========================================

# Удалить каждую вторую строку
sed 'n;d' file.txt
# n — перейти к следующей строке (чётная)
# d — удалить её
# Остаются нечётные строки

# Вывод чётных строк
sed -n 'n;p' file.txt
# n — перейти к следующей (чётной)
# p — напечатать её

# Добавить разделитель после каждой строки кроме последней
sed '$!{ n; i -------------------- }' file.txt

# ==========================================
# N — многострочная обработка
# ==========================================

# Соединить каждые ДВЕ строки в одну (через пробел)
sed 'N;s/\n/ /' file.txt
# N — добавить следующую строку (pattern space = "строка1\nстрока2")
# s/\n/ / — заменить \n на пробел → "строка1 строка2"

# Показать содержимое pattern space после N
printf "%s\n" aa bb cc dd | sed -n 'N;l'
# aa\nbb$    ← l показывает управляющие символы буквально
# cc\ndd$

# Объединить строки с обратным слешем (продолжение строки)
sed ':a;/\\$/N;s/\\\n//;ta' file.txt
# :a      — метка
# /\\$/N  — если строка заканчивается на \, добавить следующую
# s/\\\n// — удалить \ и \n
# ta      — если замена была успешна — повторить

# Найти и заменить текст разбитый на ДВЕ строки
sed 'N;s/первая строка\nвторая строка/замена/' file.txt

# Объединить ВСЕ строки файла в одну (сначала всё загружаем)
sed ':a;N;$!ba;s/\n/ /g' file.txt
# :a    — метка
# N     — добавить следующую строку
# $!ba  — если не конец файла — повторить (загрузить ещё одну)
# s/\n/ /g — заменить все \n на пробел

# Восстановить QP-кодирование (строки объединённые через =)
sed ':x ; /=$/ { N ; s/=\n//g ; bx }' encoded.txt
# :x          — метка
# /=$/ {      — если строка заканчивается на =:
#   N         — добавить следующую строку
#   s/=\n//g  — удалить = и \n (склеить)
#   bx        — повторить цикл (вдруг следующая тоже с =)
# }

# ==========================================
# D — удалить до первого \n и перезапустить цикл
# ==========================================

# Показать скользящее окно из 2 строк
printf "%s\n" 1 2 3 4 5 | sed -n 'N;P;D'
# 1
# 2
# 3
# 4
# Объяснение:
# N — добавить строку (pattern = "1\n2")
# P — напечатать до первого \n (→ "1")
# D — удалить до первого \n (pattern = "2"), перезапустить цикл
# Следующая итерация: N добавляет "3" (pattern = "2\n3"), P печатает "2"...

Переходы и метки

SED поддерживает goto-подобные переходы для построения циклов и ветвлений.

# ==========================================
# Объявление меток и безусловный переход b
# ==========================================

# :имя_метки — объявить метку
# b метка    — безусловный переход на метку
# b          — переход в КОНЕЦ цикла (пропустить оставшиеся команды)

# Ветвление: пропустить замену для строк с "SKIP"
sed '/SKIP/ b end ; s/a/A/g ; :end' file.txt
# Если строка содержит "SKIP" → перейти к :end (пропустить s/a/A/g)
# Иначе → выполнить замену, потом :end

# Пропустить несколько команд
sed '
/SKIP/ b end
s/foo/bar/g
s/baz/qux/g
s/old/new/g
:end
' file.txt

# Безусловный переход в конец цикла (b без метки)
sed '/^#/ b ; s/param/PARAM/' file.txt
# Для строк с # → перейти в конец (не применять замену)
# Для остальных → выполнить замену

# ==========================================
# Условный переход t (если s выполнилась успешно)
# ==========================================

# t метка — перейти на метку ЕСЛИ предыдущая команда s выполнила замену
# T метка — перейти на метку ЕСЛИ предыдущая s НЕ выполнила замену

# Повторять замену до тех пор пока есть вхождения
# (полезно для вложенных паттернов)
echo "aaa" | sed ':loop ; s/aa/a/ ; t loop'
# Первая итерация: s/aa/a/ → "aa" (успешно), t переходит к :loop
# Вторая итерация: s/aa/a/ → "a" (успешно), t переходит к :loop
# Третья итерация: s/aa/a/ → "a" (нет "aa", не выполнилась), t НЕ переходит
# Результат: a

# Удалить ведущие пробелы (более надёжный способ через цикл)
echo "   hello" | sed ':loop ; s/^  / / ; t loop'
# Заменяет два пробела одним, повторяет пока есть замены

# ==========================================
# Практические примеры с переходами
# ==========================================

# if-else конструкция в SED
# "Если строка с 'error' — вывести с [ERR], иначе с [OK]"
sed '
/error/ b is_error
s/^/[OK] /
b end
:is_error
s/^/[ERR] /
:end
' log.txt

# Восстановить текст с переносом строки (QP)
sed ':x ; $!N ; s/=\n// ; tx ; P ; D' encoded.txt
# :x     — метка
# $!N    — добавить следующую строку (если не конец файла)
# s/=\n// — удалить = и \n
# tx     — если замена была — перейти к :x (ещё раз проверить)
# P      — напечатать до первого \n
# D      — удалить до первого \n, перезапустить

# Удалить пустые строки в начале файла
sed '
:start
/^$/ {
    N
    b start
}' file.txt
# Пока строки пустые — добавляем следующую и возвращаемся
# Когда встречаем непустую строку — pattern space содержит все
# накопленные пустые строки + первую непустую, s/\n*// их удаляет

# Бесконечная петля (для демонстрации — НЕ запускать!)
echo "test" | sed ':loop ; b loop'   # ❌ никогда не завершится

# n и N разрывают петлю когда данные кончаются
seq 5 | sed ':loop ; n ; b loop'     # ✅ завершится на последней строке

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

По умолчанию SED использует BRE (Basic Regular Expressions). С флагом -EERE (Extended Regular Expressions).

Отличия BRE и ERE

Конструкция BRE ERE (-E)
Группа \(abc\) (abc)
Альтернатива a\|b a\|b
+ (1 или более) \+ +
? (0 или 1) \? ?
{m,n} повторения \{m,n\} {m,n}
Ссылка на группу \1, \2 \1, \2

Классы символов POSIX

Класс Описание
[[:alpha:]] Буквы (a-z, A-Z)
[[:digit:]] Цифры (0-9)
[[:alnum:]] Буквы и цифры
[[:space:]] Пробельные символы (пробел, таб, \n)
[[:blank:]] Пробел и табуляция
[[:upper:]] Заглавные буквы
[[:lower:]] Строчные буквы
[[:punct:]] Знаки пунктуации
[[:print:]] Печатаемые символы
[[:graph:]] Печатаемые символы кроме пробела
# ==========================================
# BRE — базовые регулярные выражения (по умолчанию)
# ==========================================

# Группы требуют экранирования \( \)
sed 's/\([[:alnum:]]*\) \([[:alnum:]]*\)/\2 \1/' <<< "John Doe"
# → Doe John
# \([[:alnum:]]*\) — группа 1: слово
# \([[:alnum:]]*\) — группа 2: второе слово
# \2 \1            — меняем местами

# Квантификатор + требует экранирования \+
sed 's/[0-9]\+/NUM/' <<< "foo 123 bar"
# → foo NUM bar

# ==========================================
# ERE — расширенные регулярные выражения (-E)
# ==========================================

# Группы без экранирования
sed -E 's/([[:alnum:]]+) ([[:alnum:]]+)/\2 \1/' <<< "John Doe"
# → Doe John

# Альтернатива
sed -E 's/(cat|dog)/animal/g' <<< "I have a cat and a dog"
# → I have a animal and a animal

# Квантификатор + без экранирования
sed -E 's/[0-9]+/NUM/g' <<< "foo 123 bar 456"
# → foo NUM bar NUM

# Квантификатор ? — ноль или одно вхождение
sed -E 's/colou?r/colour/g' <<< "color and colour"
# → colour and colour
# u? — ноль или одна буква u

# Повторения {m,n}
# Найти строки с 5-значными числами
sed -nE '/[0-9]{5}/p' file.txt

# Извлечь email-адреса
sed -nE 's/.*([a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}).*/\1/p' file.txt

# Форматировать дату YYYYMMDD → YYYY-MM-DD
echo "Дата: 20231225" | sed -E 's/([0-9]{4})([0-9]{2})([0-9]{2})/\1-\2-\3/'
# → Дата: 2023-12-25

# Извлечь IP-адрес
echo "Сервер 192.168.1.100 недоступен" | \
    sed -nE 's/.*([0-9]{1,3}(\.[0-9]{1,3}){3}).*/\1/p'
# → 192.168.1.100

# ==========================================
# Практические примеры регулярных выражений
# ==========================================

# Удалить HTML-теги
echo "<b>Жирный</b> и <i>курсив</i>" | sed 's/<[^>]*>//g'
# → Жирный и курсив
# [^>]* — любые символы кроме >

# Найти строки с IP-адресом
sed -nE '/^[0-9]{1,3}(\.[0-9]{1,3}){3}/p' file.txt

# Удалить строки с только пробелами и табуляцией
sed '/^[[:blank:]]*$/d' file.txt

# Найти строки с URL
sed -nE '/https?:\/\/[^ ]+/p' file.txt

# Извлечь только числа из строки
echo "abc 123 def 456 ghi" | sed -E 's/[^0-9 ]//g;s/  */ /g'
# → 123 456

# Добавить ведущий ноль к числам меньше 10
echo "Дата: 1-9-2023" | sed -E 's/\b([0-9])\b/0\1/g'
# → Дата: 01-09-2023
# \b — граница слова (работает в GNU SED)

Регистр символов (только GNU SED)

# \u — следующий символ в ВЕРХНИЙ регистр
# \U — все последующие символы в ВЕРХНИЙ регистр
# \l — следующий символ в нижний регистр
# \L — все последующие символы в нижний регистр
# \E — конец преобразования регистра

# Перевести всю строку в верхний регистр
sed 's/.*/\U&/' <<< "hello world"
# → HELLO WORLD
# & — всё найденное совпадение (вся строка), \U — в верхний регистр

# Перевести в нижний регистр
sed 's/.*/\L&/' <<< "HELLO WORLD"
# → hello world

# Каждое слово с заглавной буквы (Title Case)
sed 's/\b./\u&/g' <<< "hello world foo bar"
# → Hello World Foo Bar
# \b. — первый символ каждого слова
# \u  — в верхний регистр

# Первая буква заглавная, остальные строчные (Sentence case)
sed 's/.*/\u\L&/' <<< "hELLO wORLD"
# → Hello world
# \u\L — первый символ заглавный, далее все строчные

# Заглавная только первая буква каждого слова + остальные строчные
sed -E 's/([[:alpha:]])([[:alpha:]]*)/\u\1\L\2/g' <<< "hELLO wORLD"
# → Hello World

# Перевести в верхний регистр только первое слово
sed 's/[[:alpha:]]*/\U&/' <<< "hello world"
# → HELLO world
# [[:alpha:]]* — первое слово (до первого не-буквенного символа)

# Изменить регистр только в части строки
sed 's/\(ERROR\)/\L\1/' <<< "This is an ERROR message"
# → This is an error message

# Комбинация: имя в заглавном регистре, фамилия в нижнем
echo "ИВАН ИВАНОВ" | sed 's/\([^ ]*\) \([^ ]*\)/\u\L\1 \L\2/'
# → Иван иванов

Подстановки командной оболочки в микропрограмме

# Одинарные кавычки — Shell НЕ интерпретирует содержимое
sed 's/old/new/' file.txt    # $ и другие спецсимволы не интерпретируются

# Двойные кавычки — Shell интерпретирует содержимое
# Позволяет использовать переменные и подстановки команд
version="2.0.0"
sed "s/VERSION/$version/" template.txt

# Подстановка команды прямо в sed
sed "s/DATE/$(date +%Y-%m-%d)/" template.txt

# Переменная с путём — нужно экранировать /
path="/usr/local/bin"
safe_path="${path//\//\\/}"    # заменить / на \/
sed "s|OLD_PATH|$path|" file.txt   # лучше — сменить разделитель на |

# Склейка одинарных и двойных кавычек для безопасной подстановки
pattern="[0-9]+"
sed -n '\''/'"$pattern"'/'\''p' file.txt
# Сложно читается — лучше использовать -e:
sed -n -e "/$pattern/p" file.txt

# Переменная как разделитель (нестандартный трюк)
SEP="|"
sed "s${SEP}old${SEP}new${SEP}g" file.txt

# Имитация grep с поддержкой переменной шаблона
search_pattern="error|warning"
sed -nE "/$search_pattern/p" log.txt

# Безопасная передача переменной (без риска инъекции)
# Если переменная содержит специальные символы SED
user_input="fo/o"
# ОПАСНО — если user_input содержит /:
sed "s/$user_input/bar/" file.txt   # ❌ сломается
# БЕЗОПАСНО — через переменную SED:
sed -e "s|$user_input|bar|" file.txt  # ✅ если | не в строке

# Сгенерировать sed-команды из другого файла и выполнить
# Пример: users.txt содержит "ID NAME", создаём замены ID→NAME
sed $(awk '{print "-e s/"$1"/"$2"/"}' users.txt) contacts.txt

Скрипты SED в файлах

# ==========================================
# Создание и запуск скрипта
# ==========================================

cat << 'EOF' > cleanup.sed
# Скрипт очистки конфигурационного файла
# Использование: sed -nf cleanup.sed config.txt

# Удалить inline комментарии (от # до конца строки)
s/#.*//

# Удалить хвостовые пробелы и табуляцию
s/[[:blank:]]*$//

# Удалить пустые строки
/^$/ d

# Напечатать оставшееся
p
EOF

# Запустить
sed -nf cleanup.sed /etc/ssh/sshd_config

# Запустить для нескольких файлов
sed -nf cleanup.sed /etc/nginx/nginx.conf /etc/ssh/sshd_config

# ==========================================
# Несколько скриптов
# ==========================================

# Скрипты выполняются последовательно (как -e)
sed -f normalize.sed -f format.sed -f cleanup.sed file.txt

# ==========================================
# SED как исполняемый скрипт (shebang)
# ==========================================

cat << 'EOF' > transform.sed
#!/bin/sed -f
# Трансформировать CSV: заменить разделитель , на ;
# и перевести заголовки в верхний регистр
1 s/.*/\U&/         # первая строка (заголовок) — в верхний регистр
s/,/;/g             # заменить разделитель
EOF

chmod +x transform.sed
cat data.csv | ./transform.sed

# ==========================================
# Шаблонный препроцессор
# ==========================================

cat << 'EOF' > report.rules.sed
# Правила форматирования отчёта

# Заголовок нулевого уровня
/h\.0\..*/ {
    i ================================================================================
    a ================================================================================
    s/h\.0\.\(.*\)/ \U\1/
}

# Заголовок первого уровня
/h\.1\..*/ {
    i --------------------------------------------------------------------------------
    a --------------------------------------------------------------------------------
    s/h\.1\.\(.*\)/  \1/
}

# Вставить файл лицензии
/LICENSE/ {
    r license.txt
    d
}

# Напечатать результат
p
EOF

sed -nf report.rules.sed template.txt > report.txt

Практические примеры

Очистка конфигурационных файлов

# Показать конфиг без комментариев и пустых строк
sed -n '
    s/#.*//         # удалить комментарии
    s/[[:blank:]]*$//  # удалить хвостовые пробелы
    /^$/ d          # удалить пустые строки
    p               # напечатать оставшееся
' /etc/ssh/sshd_config

# Результат — только активные параметры:
# PasswordAuthentication no
# UsePAM yes
# X11Forwarding yes

# Одной строкой
sed -n 's/#.*//;s/[[:blank:]]*$//;/^$/ d;p' /etc/ssh/sshd_config

# Показать только строки с параметрами (ключ=значение)
sed -nE '/^[[:alpha:]_]+=/p' config.ini

Редактирование конфигурационных файлов

# Изменить значение параметра
sed -i 's/^timeout=.*/timeout=30/' config.ini
# ^ — начало строки (чтобы не зацепить другие параметры)
# .* — всё что идёт после = (старое значение)

# Раскомментировать параметр (убрать # в начале)
sed -i 's/^#\(PasswordAuthentication\)/\1/' /etc/ssh/sshd_config
# \(...\) — группа захвата
# \1 — подставить содержимое группы (без #)

# Закомментировать параметр
sed -i 's/^\(PermitRootLogin\)/#\1/' /etc/ssh/sshd_config

# Добавить параметр в секцию конфига
sed -i '/^\[database\]/a host=localhost\nport=5432' config.ini

# Заменить значение только если оно равно конкретному
sed -i '/^log_level=debug/c log_level=info' config.ini

# Обновить URL во всех конфигах рекурсивно
find /etc/myapp -name "*.conf" | \
    xargs sed -i 's|http://old.server.com|https://new.server.com|g'

# Раскомментировать всю секцию (строки начинающиеся с #param)
sed -i '/^\[section\]/,/^\[/ s/^#//' config.ini
# От [section] до следующей секции: убрать # в начале строки

Форматирование текста

# Двойной интервал — добавить пустую строку после каждой
sed 'G' file.txt

# Убрать двойные пустые строки (оставить одинарные)
sed '/^$/{ N; /^\n$/d }' file.txt
# Если строка пустая и следующая тоже — удалить
# (оставит только первую из нескольких подряд пустых строк)

# Добавить отступ к элементам списка (строки начинающиеся с *)
sed 's/^\* /   * /' list.txt

# Подчеркнуть заголовок (добавить строку из тире после)
sed '/^Заголовок/a ─────────────────────' text.txt

# Добавить рамку вокруг каждой строки
sed 's/.*/| & |/' file.txt

# Выровнять по левому краю (убрать ведущие пробелы)
sed 's/^[[:space:]]*//' file.txt

# Пронумеровать строки красиво
sed '=' file.txt | sed 'N;s/\n/\t/' | \
    sed 's/^/  /;s/ *\(.\{4\}\)/\1/'

# Добавить блок BEFORE/AFTER вокруг каждого абзаца
sed 's/^/    /' paragraphs.txt   # отступ для каждой строки

Работа с логами

# Извлечь строки с ошибками
sed -n '/\[ERROR\]/p' app.log

# Извлечь блок между метками START и END
sed -n '/\[START\]/,/\[END\]/p' app.log

# Удалить строки DEBUG (уменьшить объём лога)
sed '/\[DEBUG\]/d' app.log > filtered.log

# Заменить IP-адреса для безопасного шаринга логов
sed -E 's/[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}/X.X.X.X/g' access.log

# Добавить метку [CRITICAL] к строкам с кодом 500
sed '/HTTP\/[0-9.]* 500/ s/^/[CRITICAL] /' access.log

# Извлечь только HTTP-коды ответа из Nginx лога
sed -nE 's/.*" ([0-9]{3}) .*/\1/p' access.log | sort | uniq -c | sort -rn

# Показать последние 50 ошибок (tail + sed)
tail -1000 app.log | sed -n '/ERROR/p' | tail -50

# Раскрасить вывод (добавить ANSI-коды)
sed 's/ERROR/\x1b[31m&\x1b[0m/;s/WARNING/\x1b[33m&\x1b[0m/' log.txt
# \x1b[31m — красный цвет
# \x1b[33m — жёлтый цвет
# \x1b[0m  — сброс цвета

Обработка CSV и таблиц

# Пример data.csv:
# name,age,city,salary
# Alice,30,Moscow,75000
# Bob,25,SPb,60000

# Удалить заголовок
sed '1d' data.csv

# Сохранить только заголовок
sed -n '1p' data.csv

# Добавить вычисляемый столбец (нет арифметики — добавим текст)
sed '1s/$/,department/;2,$s/$/,Engineering/' data.csv

# Заменить разделитель , на ;
sed 's/,/;/g' data.csv

# Удалить последний столбец
sed 's/,[^,]*$//' data.csv

# Вывести только строки с "Moscow"
sed -n '/,Moscow,/p' data.csv

# Заменить пустые поля на "N/A"
sed 's/,,/,N\/A,/g;s/,$/,N\/A/' data.csv

Сравнение файлов и формирование данных

# Заменить ID на имена используя внешний файл с маппингом
# users.txt: "1001 Alice Brown"
# contacts.txt: "1001 3L24 555-0124"

sed $(sed -n '3,$p' users.txt | \
    awk '{print "-e s/"$1"/"$2"_"$3"/"}') \
    contacts.txt | \
    tr '_' ' '
# Объяснение:
# sed извлекает строки данных из users.txt
# awk создаёт команды вида: -e s/1001/Alice_Brown/
# внешний sed применяет эти замены к contacts.txt
# tr заменяет _ на пробел (чтобы имя с пробелом работало)

Редактирование нескольких файлов

# Замена в нескольких файлах — вывод в STDOUT
sed 's/old/new/g' file1.txt file2.txt file3.txt

# Замена прямо в файлах (in-place)
sed -i 's/old/new/g' file1.txt file2.txt

# С резервными копиями
sed -i.bak 's/old/new/g' file1.txt file2.txt
# Создаёт: file1.txt.bak, file2.txt.bak

# Создать разные суффиксы — через цикл
for f in *.conf; do
    sed -i".${f}.bak" 's/old/new/g' "$f"
done

# Рекурсивное редактирование всех .txt файлов
find . -type f -name "*.txt" -exec sed -i 's/old/new/g' {} \;

# Через xargs — быстрее для большого числа файлов
find . -type f -name "*.txt" | xargs sed -i 's/old/new/g'

# С фильтром — только файлы содержащие "old"
grep -rl "old" . | xargs sed -i 's/old/new/g'

# Обработать каждый файл отдельно (не конкатенировать)
sed -s 's/old/new/g' file1.txt file2.txt

# Обработать файлы с Windows CRLF
sed -b 's/\r//' file.txt    # бинарный режим

Полезные однострочники

# ==========================================
# Работа со строками
# ==========================================

# Удалить пустые строки
sed '/^$/d' file.txt

# Удалить строки состоящие только из пробелов
sed '/^[[:space:]]*$/d' file.txt

# Удалить комментарии и пустые строки
sed '/^[[:space:]]*#/d;/^[[:space:]]*$/d' file.txt

# Удалить повторяющиеся ПОДРЯД ИДУЩИЕ строки (аналог uniq)
sed '$!N;/^\(.*\)\n\1$/!P;D' file.txt

# Вывести строки в обратном порядке (аналог tac)
sed -n '1!G;h;$p' file.txt

# Добавить нумерацию строк (аналог cat -n)
sed '=' file.txt | sed 'N;s/\n/\t/'

# ==========================================
# Извлечение данных
# ==========================================

# Вывести строки с N по M
sed -n '5,10p' file.txt

# Вывести первые N строк (аналог head -5)
sed '6,$d' file.txt

# Вывести последние 3 строки (аналог tail -3)
sed -n -e :a -e '$p;N;3,$D;ba' file.txt

# Вывести строку ПЕРЕД строкой с "pattern"
sed -n '/pattern/{x;p;d};x' file.txt

# Вывести строку ПОСЛЕ строки с "pattern"
sed -n '/pattern/{n;p}' file.txt

# Вывести строки С контекстом (±2 строки вокруг "error")
sed -n '/error/{
    # сохранить текущую
    h
    # взять 2 строки до
    x
    # напечатать контекст
    p
}' file.txt

# ==========================================
# Форматирование
# ==========================================

# Двойной интервал (пустая строка после каждой)
sed 'G' file.txt

# Убрать двойной интервал
sed 'n;/^$/d' file.txt

# Добавить строку в начало файла
sed -i '1i # Добавленная первая строка' file.txt

# Добавить строку в конец файла
sed -i '$a # Добавленная последняя строка' file.txt

# Удалить HTML-теги
sed 's/<[^>]*>//g' file.html

# Удалить ANSI escape-коды (цвета)
sed 's/\x1b\[[0-9;]*m//g' colored.txt

# Заменить табы на 4 пробела
sed 's/\t/    /g' file.txt

# Удалить Windows CRLF
sed -i 's/\r$//' file.txt

# Объединить все строки в одну
sed ':a;N;$!ba;s/\n/ /g' file.txt

# Разбить строку по N символов (каждые 80 символов — новая строка)
sed 's/.\{80\}/&\n/g' long_line.txt

# ==========================================
# Подсчёт
# ==========================================

# Количество строк в файле
sed -n '$=' file.txt

# Количество непустых строк
sed '/^$/d' file.txt | sed -n '$='

# Количество строк с "pattern"
sed -n '/pattern/p' file.txt | sed -n '$='

Escape-последовательности

Последовательность Символ
\n Новая строка
\t Табуляция
\r Возврат каретки
\\ Обратный слеш
\xNN Символ в шестнадцатеричной нотации (GNU SED)
\u Следующий символ в верхний регистр (GNU SED)
\U Все последующие в верхний регистр (GNU SED)
\l Следующий символ в нижний регистр (GNU SED)
\L Все последующие в нижний регистр (GNU SED)
\E Конец преобразования регистра (GNU SED)
# Вставить табуляцию в строку замены
sed 's/,/\t/g' data.csv    # CSV → TSV

# Вставить новую строку в замену
sed 's/;/\n/g' file.txt    # каждый ; на новую строку

# Hex-код символа
sed 's/\x09/    /g' file.txt    # \x09 = TAB → 4 пробела

# Комбинация регистра
sed 's/\b\(.\)/\u\1/g' text.txt    # каждое слово с заглавной

Сравнение BRE / ERE / GNU SED

Возможность BRE ERE (-E) GNU SED
Группы \(...\) (...) оба варианта
Квантификатор + \+ + оба
Квантификатор ? \? ? оба
Альтернатива a\|b a\|b оба
{m,n} \{m,n\} {m,n} оба
\U, \L, \u, \l
addr~step
addr,+N
Метки и переходы ✅ расширено
-i in-place зависит от реализации зависит
-s отдельные файлы

💡 macOS: стандартный BSD sed отличается от GNU sed. Для GNU sed используй gsed (установить через brew install gnu-sed). Основные отличия: в -i нужен обязательный суффикс (sed -i '' 's/a/b/'), нет \+, \?, addr~step.


Коды выхода

Код Значение
0 Успешное выполнение
1 Синтаксическая ошибка в микропрограмме
2 Ошибка при работе с файлами
# Проверить код выхода
sed 's/old/new/' file.txt
echo "Код выхода: $?"    # → 0

# Использовать в условиях скрипта
if sed -n '/pattern/q0;$q1' file.txt 2>/dev/null; then
    echo "Паттерн найден"
else
    echo "Паттерн не найден"
fi

# Обработка ошибок
sed -i.bak 's/old/new/g' file.txt || {
    echo "Ошибка! Восстанавливаем из бэкапа..."
    mv file.txt.bak file.txt
}

Шпаргалка — быстрый справочник

# ЗАМЕНА
sed 's/old/new/'           # первое вхождение
sed 's/old/new/g'          # все вхождения
sed 's/old/new/2'          # только второе вхождение
sed 's/old/new/2g'         # начиная со второго
sed 's/old/new/gi'         # все, без учёта регистра
sed '3s/old/new/'          # только в строке 3
sed '/pat/s/old/new/'      # в строках с "pat"
sed '1 !s/old/new/'        # во всех строках кроме первой
sed -i 's/old/new/g' f     # изменить файл на месте
sed -i.bak 's/old/new/' f  # с резервной копией

# ВЫВОД
sed -n 'p'                 # явная печать (подавить авто)
sed -n '3p'                # строка 3
sed -n '3,7p'              # строки 3-7
sed -n '$p'                # последняя строка
sed -n '/pat/p'            # строки с "pat"
sed -n '/a/,/b/p'          # от строки с "a" до "b"
sed -n '1~2p'              # нечётные строки (GNU)
sed -n '3,+2p'             # строка 3 и ещё 2 после (GNU)

# УДАЛЕНИЕ
sed '3d'                   # строка 3
sed '3,5d'                 # строки 3-5
sed '/pat/d'               # строки с "pat"
sed '/^$/d'                # пустые строки
sed '/^#/d'                # строки-комментарии
sed '2,4 !d'               # всё КРОМЕ строк 2-4

# ВСТАВКА И ДОБАВЛЕНИЕ
sed '3i строка'            # вставить ПЕРЕД строкой 3
sed '3a строка'            # добавить ПОСЛЕ строки 3
sed '1i строка'            # добавить в начало файла
sed '$a строка'            # добавить в конец файла
sed '/pat/i строка'        # вставить перед "pat"
sed '/pat/a строка'        # добавить после "pat"
sed '3c строка'            # заменить строку 3 целиком

# АДРЕСАЦИЯ
sed -n '1~2p'              # нечётные строки
sed -n '2~2p'              # чётные строки
sed -n '5,+3p'             # строка 5 и ещё 3
sed '2,4 !p'               # всё КРОМЕ строк 2-4
sed -n '$='                # количество строк в файле

# ФАЙЛЫ
sed -f script.sed file     # скрипт из файла
sed -e 'cmd1' -e 'cmd2'    # несколько команд
sed -n '/pat/w out.txt'    # записать в файл
sed '3r insert.txt'        # вставить файл после строки 3
find . -name "*.txt" -exec sed -i 's/a/b/g' {} \;

# БУФЕРЫ
sed 'G'                    # добавить пустую строку после каждой
sed -n '1!G;h;$p'          # строки в обратном порядке
sed 'N;s/\n/ /'            # объединить пары строк

# РЕГУЛЯРНЫЕ ВЫРАЖЕНИЯ
sed -E 's/(foo|bar)/baz/g'      # ERE: альтернатива
sed 's/\([[:alnum:]]*\).*/\1/'  # BRE: первое слово строки
sed -E 's/([0-9]{4})-([0-9]{2})-([0-9]{2})/\3.\2.\1/' # дата

# РЕГИСТР (только GNU SED)
sed 's/.*/\U&/'            # всё в верхний регистр
sed 's/.*/\L&/'            # всё в нижний регистр
sed 's/\b./\u&/g'          # каждое слово с заглавной

# ПЕРЕХОДЫ (GNU SED)
sed ':a ; s/aa/a/ ; t a'   # цикл до тех пор пока работает s
sed '/pat/b end ; s/a/b/ ; :end'  # пропустить для "pat"