# sed - потоковый редактор текста _Команды · LinuxLab Knowledge Base_ **TL;DR:** sed - потоковый редактор: каждой строке применяет команды (`s/a/b/`, `d`, `p`, ...). `-i` правит файл на месте; `-E` включает ERE; адресный диапазон `/start/,/end/` фильтрует блок. Hold space - вторая память. ## Зачем sed Когда надо переписать строки в потоке (логе, конфиге, выводе команды), sed - самый быстрый инструмент: одна команда, без временных файлов, без Python'а. Работает построчно (line-oriented), без буферизации всего входа в память. Идеально для пайпов и `find -exec`. Если задача шире построчной замены - бери [[cmd-awk|awk]] или Python. ## Базовый синтаксис ``` sed [OPTIONS] 'SCRIPT' [FILE...] sed [OPTIONS] -f script.sed [FILE...] ``` Без файла читает stdin. Скрипт - набор команд через `;` или `-e`. По дефолту печатает каждую обработанную строку (если не отключить `-n`). ## Команда `s` - замена Самая частая команда: ```bash sed 's/old/new/' file.txt # первое совпадение в строке sed 's/old/new/g' file.txt # все совпадения (global) sed 's/old/new/2' file.txt # только 2-е совпадение sed 's/old/new/gi' file.txt # global + case-insensitive ``` Разделитель не обязан быть `/` - удобно для путей: ```bash sed 's|/old/path|/new/path|g' file sed 's#http://#https://#g' urls.txt ``` ### `-E` для ERE По дефолту sed использует BRE, как у `grep` без `-E`. То есть `+`, `?`, `(`, `)`, `{`, `}` нужно экранировать. Включи `-E` (или `-r` в GNU): ```bash sed -E 's/(error|warn): (.*)/[\1] \2/' app.log ``` Группы `\1`, `\2` доступны и в BRE, но скобки в BRE надо `\(\)`. ## In-place редактирование `-i` ```bash sed -i 's/foo/bar/g' file.txt # GNU sed sed -i.bak 's/foo/bar/g' file.txt # с backup file.txt.bak sed -i '' 's/foo/bar/g' file.txt # macOS BSD sed - пустое расширение обязательно ``` **Кроссплатформенный кошмар:** GNU `sed -i` принимает опциональный аргумент слитно (`sed -i.bak`), BSD - требует пробел и обязательное расширение (`sed -i '' ...`). На macOS чаще ставят `gsed` через brew. ## Адресация - где применять команду ```bash sed '5d' file.txt # удалить 5-ю строку sed '5,10d' file # удалить строки 5-10 sed '/^#/d' file # удалить комментарии sed '/start/,/end/d' file # удалить блок от start до end включительно sed '$d' file # удалить последнюю sed '1,/^$/d' file # удалить с начала до первой пустой sed -n '/ERROR/p' file # напечатать только ERROR-строки (как grep) ``` Адресные диапазоны - убийственно полезная фича: вырезаешь куски конфигов, фильтруешь блоки логов между маркерами. ## Команды кроме `s` | Команда | Что делает | |---------|------------| | `d` | удалить (не печатать) текущую строку | | `p` | напечатать (имеет смысл с `-n`) | | `q` | quit - сразу выйти | | `=` | напечатать номер строки | | `i\TEXT`| вставить TEXT перед строкой | | `a\TEXT`| добавить TEXT после строки | | `c\TEXT`| заменить целиком на TEXT | | `y/abc/ABC/` | посимвольная транслитерация (как `tr`) | | `r FILE`| вставить содержимое файла после совпавшей строки | | `w FILE`| записать совпавшие строки в FILE | ```bash sed '/^server {/i\# auto-managed' nginx.conf # коммент перед каждым server-блоком sed -n '5,10p' file # быстрее `head -10 | tail -6` sed '0,/foo/{s/foo/bar/}' file # заменить ТОЛЬКО первое foo во всём файле ``` ## Multi-line: pattern space + hold space sed работает с двумя буферами: - **pattern space** - текущая строка (по умолчанию) - **hold space** - вторичная память, между обработкой строк Команды между ними: - `h` - скопировать pattern → hold (затирает) - `H` - добавить pattern в hold (через \n) - `g` - скопировать hold → pattern (затирает) - `G` - добавить hold в pattern - `x` - swap Классический трюк - реверс строк (`tac`): ```bash sed '1!G;h;$!d' file ``` Это уже территория где [[cmd-awk|awk]] или Python читаются легче. ## Когда что-то пошло не так - **`sed: -i may not be used with stdin`** - после `-i` нужен файл, не пайп - **`sed: 1: "...": invalid command code` на macOS** - BSD-sed; ставь `brew install gnu-sed` и зови `gsed` - **`unterminated 's' command`** - забыл закрывающий `/` или экранирование спецсимвола в шаблоне - **Замена не сработала, хотя `grep` находит** - `sed` без `-E` ждёт BRE; группа `(foo|bar)` без `-E` - литерал - **In-place портит symlink** - GNU sed по дефолту переименовывает, разрывая ссылку. Используй `--follow-symlinks` - **Кодировка полетела** - sed работает побайтово; для UTF-8 с мульти-байтными классами `[[:alpha:]]` нужен правильный locale (`LC_ALL=ru_RU.UTF-8`) ## Альтернативы - **`awk`** - когда работаешь с полями, нужен счётчик, агрегация - **`perl -pe`/`perl -i`** - PCRE, lookahead, более вменяемая кросс-платформенность - **`sd`** (Rust) - современный CLI с PCRE-дефолтом и понятным синтаксисом ## Команды ```bash sed -i.bak 's/foo/bar/g' config.yml ``` In-place заменить все foo на bar; backup config.yml.bak ```bash sed -n '/^ERROR/,/^$/p' app.log ``` Напечатать блоки от ERROR до пустой строки - точечный grep по контексту ```bash sed '/^#/d;/^$/d' /etc/nginx/nginx.conf ``` Убрать комментарии и пустые строки - чистый конфиг как он применён ```bash sed -E 's|http://([^/]+)/.*|\1|' urls.txt | sort -u ``` Извлечь host из URL - capture-группа + ERE ```bash find . -name '*.py' -exec sed -i 's/print /print(/g' {} + ``` Массовая замена в дереве - для миграций между версиями ```bash sed '5,10!d' file.txt ``` Оставить только строки 5-10; `!d` = «не удалять диапазон, остальное удалить» ```bash echo 'a b c' | sed -E 's/(\w+) (\w+) (\w+)/\3 \2 \1/' ``` Переставить три слова - демо capture-групп ## См. также - [grep - поиск строк по шаблону](/kb/cmd-grep.md) - [awk - обработка структурированного текста по полям](/kb/cmd-awk.md) - [bash-скрипты - основы и идиомы](/kb/bash-scripting.md) - [xargs и find -exec - массовые операции](/kb/xargs-and-find-exec.md) - [find - поиск файлов по предикатам](/kb/cmd-find.md)