sed: потоковый редактор текста
sed читает поток построчно и применяет к каждой строке набор команд-редактирования — это редактирование текста без открытия редактора.
sed (stream editor) — неинтерактивный потоковый редактор: он берёт вход построчно, держит текущую строку в так называемом «рабочем пространстве» (pattern space), выполняет над ней команды и по умолчанию печатает результат.
Когда вам нужно заменить подстроку в сотне файлов, вырезать блок строк из конфига, добавить заголовок в начало вывода или превратить CSV в другой формат — открывать редактор и кликать мышкой бессмысленно. sed делает это одной строкой и встраивается в конвейер. Это рабочая лошадь любого скрипта развёртывания, миграции конфигов и обработки логов на лету.
Подстановка s/// — главная команда
99% использования sed — это команда подстановки s/что/на_что/. По умолчанию она заменяет первое вхождение в каждой строке:
echo "кот и кот" | sed 's/кот/пёс/'
# пёс и кот
# заменить во входном файле и напечатать в stdout (файл не меняется)
sed 's/localhost/127.0.0.1/' /etc/hosts
Флаги ставятся после закрывающего слэша и меняют поведение:
| Флаг | Что делает |
g | global — заменить все вхождения в строке, а не только первое |
i | без учёта регистра (GNU sed: I тоже работает) |
p | напечатать строку, если подстановка сработала (часто с -n) |
2 | число: заменить только N-е вхождение; 2g — со второго и далее |
echo "кот и кот и кот" | sed 's/кот/пёс/g'
# пёс и пёс и пёс
echo "Hello HELLO hello" | sed 's/hello/hi/gi'
# hi hi hi
Разделитель можно менять
Если меняете пути с кучей слэшей, разделитель / заставляет экранировать каждый \/. Возьмите любой другой символ сразу после s — обычно # или |:
# неудобно: sed 's/\/usr\/local/\/opt/'
sed 's#/usr/local#/opt#' paths.txt
Адресация: к каким строкам применять
Перед командой можно указать адрес — какие строки трогать. Без адреса команда применяется ко всем строкам.
# только строка 3
sed '3 s/foo/bar/' file.txt
# диапазон строк 2..5
sed '2,5 s/foo/bar/' file.txt
# от строки 10 до конца файла ($ — последняя строка)
sed '10,$ d' file.txt
# только строки, совпавшие с регуляркой /ERROR/
sed '/ERROR/ s/^/!!! /' app.log
# диапазон между двумя шаблонами включительно
sed '/BEGIN/,/END/ d' config.txt
Восклицательный знак ! после адреса инвертирует выбор — «ко всем, КРОМЕ»:
# удалить все строки, КРОМЕ содержащих слово keep
sed '/keep/ !d' file.txt
Удаление, вставка, дозапись
Кроме s есть однобуквенные команды редактирования строк:
| Команда | Действие |
d | delete — удалить строку (не печатать её) |
p | print — напечатать строку (с -n — только нужные) |
i\текст | insert — вставить строку ПЕРЕД текущей |
a\текст | append — дозаписать строку ПОСЛЕ текущей |
c\текст | change — заменить всю строку целиком |
# удалить пустые строки
sed '/^$/d' file.txt
# удалить строки-комментарии (начинаются с #) и пустые
sed -E '/^\s*(#|$)/d' nginx.conf
# вставить строку перед первой
sed '1i\# Автосгенерировано, не редактировать' config.ini
# дозаписать строку после строки с [server]
sed '/\[server\]/a\port = 8080' config.ini
Печать только нужного: -n и p
Опция -n отключает автопечать — sed молчит, пока вы явно не скажете p. Это превращает sed в инструмент выборки, похожий на grep, но с диапазонами:
# напечатать строки 20..25
sed -n '20,25 p' big.log
# напечатать блок от старта до ошибки
sed -n '/Started/,/Error/ p' app.log
# заменить и напечатать ТОЛЬКО изменённые строки
sed -n 's/v1\.0/v2.0/p' release.txt
Редактирование на месте: -i
По умолчанию sed ничего не меняет в файлах — он печатает результат в stdout. Флаг -i (in-place) перезаписывает файл. Это мощно и опасно: ошибка в выражении испортит файл без возможности отката.
# опасно: правка без копии
sed -i 's/DEBUG/INFO/g' app.conf
# безопасно: -i с суффиксом делает резервную копию app.conf.bak
sed -i.bak 's/DEBUG/INFO/g' app.conf
# правка во ВСЕХ .conf-файлах каталога
sed -i.bak 's/timeout = 30/timeout = 60/' *.conf
Важно про переносимость: в GNU sed (Linux) пишут -i.bak или -i без аргумента. В BSD sed (macOS) -i требует аргумент-суффикс, поэтому sed -i '' ... с пустой строкой. Скрипты, рассчитанные на Linux, на маке ломаются именно здесь.
Скрипты sed: несколько команд
Команды можно объединять через -e либо через точку с запятой, либо через файл -f script.sed. Несколько преобразований за один проход:
sed -e 's/red/green/' -e 's/cat/dog/' -e '/^#/d' file.txt
# то же через ;
sed 's/red/green/; s/cat/dog/; /^#/d' file.txt
Для сложных преобразований кладут команды в файл — это читаемее и версионируется в git:
cat > cleanup.sed <<'EOF'
/^#/d
/^$/d
s/[[:space:]]*$//
EOF
sed -f cleanup.sed messy.conf
Как это работает под капотом
sed работает как простая виртуальная машина с двумя буферами. Pattern space — буфер текущей строки: sed читает строку (без завершающего \n), прогоняет через неё весь список команд по порядку, затем (если не было -n) печатает содержимое и переходит к следующей строке. Есть и второй буфер — hold space, в который можно сохранять строки командами h/H и доставать через g/G; именно так делают многострочные трюки вроде разворота файла. Регулярные выражения по умолчанию — POSIX BRE (basic), где +, ?, (, | нужно экранировать обратным слэшем; флаг -E (или -r в GNU) включает расширенные ERE, и тогда (group)+ работает без слэшей. Понимание модели «строка за строкой, два буфера» объясняет, почему sed эффективен на гигабайтных файлах: он никогда не держит в памяти больше одной-двух строк.
Частые ошибки
- Забыли флаг
g— заменилось только первое вхождение в строке, а казалось, что все. Это самая частая «загадка» sed. - BRE против ERE. Написали
sed 's/(a|b)/x/'и удивляетесь, что не сработало: в BRE это литералы. Нужен-Eлибо экранирование\(a\|b\). - Слэши в путях. Подстановка путей через
/-разделитель превращается в частокол\/. Меняйте разделитель на#. -iбез копии. Перезаписали файл кривым выражением — отката нет. Привыкайте к-i.bakи проверяйте выражение без-iсначала.- BSD vs GNU. Скрипт с
sed -i 's/.../.../'работает на Linux и падает на macOS. Для переносимости либо задавайте суффикс всегда, либо ставьте GNU-версию (gsed).
Итоги
- sed — потоковый редактор: читает построчно, применяет команды, печатает результат.
s/что/чем/флаги— ядро инструмента;gзаменяет все вхождения,iигнорирует регистр,pпечатает совпавшее.- Адреса (
3,2,5,/regex/,!) задают, к каким строкам применять команду. d/i/a/cудаляют, вставляют и заменяют целые строки;-n+pделают выборку.-iправит файл на месте — всегда с резервной копией-i.bak; помните про различие GNU и BSD.