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

Флаги ставятся после закрывающего слэша и меняют поведение:

ФлагЧто делает
gglobal — заменить все вхождения в строке, а не только первое
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 есть однобуквенные команды редактирования строк:

КомандаДействие
ddelete — удалить строку (не печатать её)
pprint — напечатать строку (с -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.
Проверьте себя
1. Команда echo "a a a" | sed 's/a/b/' напечатает:
Ab b b
Bb a a
Ca a b
Dничего
2. Чем безопаснее всего редактировать файл на месте, сохранив возможность отката?
Ased -i 's/x/y/' file
Bsed -i.bak 's/x/y/' file
Csed -n 's/x/y/' file
Dsed -e 's/x/y/' file