Перейти к содержимому

core.line

← Каталог модулей

soul-sidesoulstack.filesfilesroot требуется

Построчная in-place правка существующего файла (lineinfile-эквивалент).

core.line изменяет отдельные строки файла, а не перезаписывает его целиком (как core.file): present добавляет строку или заменяет первую матчащую по regexp, absent удаляет все матчащие. Запись атомарна (temp + rename), по умолчанию mode/owner/group существующего файла сохраняются. Намеренно урезанный безопасный MVP: regexp — частичное совпадение (RE2, без backrefs), replace только первого совпадения. Soul-side.

Требования

  • Rootтребуется
  • Сторонаsoul-side
  • Коллекцияsoulstack.files
  • Категорияfiles
Capabilitiesfs_write_root

Состояния

core.line.present — Строка line присутствует в файле. С regexp — первая матчащая строка заменяется на line; без regexp — точная строка line добавляется, если её ещё нет.

Меняется, когда

Строка добавлена или заменена.

Не меняется, когда

Первое совпадение уже равно line.

Параметры

ПараметрТипОбяз. / дефолтОписание
pathstringrequiredЦелевой файл.
linestringoptionalТочная строка, которой управляем (обязательна для present).
regexpstringoptionalRE2-паттерн: первая матчащая строка заменяется на line.
insertafterstringoptionalЛитерал-якорь или EOF — куда вставлять (взаимоисключаемо с insertbefore).
insertbeforestringoptionalЛитерал-якорь или BOF — куда вставлять (взаимоисключаемо с insertafter).
createbooloptionalСоздать файл, если отсутствует.
modestringoptionalПрава при create, восьмеричная форма, напр. "0644".
ownerstringoptionalВладелец при create (имя пользователя).
groupstringoptionalГруппа-владелец при create (имя группы).

Пример — Заменить существующую строку по regexp

- name: Ensure PasswordAuthentication is off in sshd_config
module: core.line.present
params:
path: /etc/ssh/sshd_config
regexp: '^#?PasswordAuthentication'
line: 'PasswordAuthentication no'

Output

ПолеТипОписание
pathstring
matchedbool
replacedbool

Заметки

  • regexp — главный источник drift: применяется к каждой логической строке без терминатора \n через Go regexp.MatchString, то есть частичное совпадение (не «вся строка целиком», если вы сами не поставили якоря ^…$). CRLF не нормализуется — \r остаётся частью строки и участвует в матчинге. Backrefs не поддержаны. Невалидный паттерн — ошибка до запуска (regexp.Compile на Validate).
  • present с regexp при множественном совпадении заменяет только первую матчащую строку, остальные не трогает; в output добавляется warning с числом совпавших. Это сознательный безопасный выбор, а не баг.
  • Якоря insertafter/insertbefore (кроме EOF/BOF) — точное совпадение строки (литерал), не regexp: позиция вставки предсказуема. Ненайденный якорь → fallback на конец файла (EOF).
  • Запись атомарна (temp + rename), не in-place truncate. По умолчанию mode/owner/group существующего файла сохраняются; явные mode/owner/group для present — override. absent их не принимает и всегда сохраняет текущие.
  • Финальный перевод строки исходного файла сохраняется; пустой файл не превращается в файл с пустой строкой. Подпроцессов не запускает — чтение, редактирование и запись in-process, без shell.

Безопасность и умолчания

  • regexp правит чужой файл частичным совпадением — главный источник опасной правки: без ^…$ он матчит подстроку, а в absent удаляет все совпавшие строки. Слишком широкий паттерн (особенно из недоверенной интерполяции) снесёт больше, чем задумано; пустой паттерн в absent матчит каждую строку → удалит весь файл.
  • ReDoS не грозит, backrefs запрещены: движок Go regexp (RE2) даёт линейное время матчинга (catastrophic backtracking невозможен), паттерн компилируется на Validate. Backrefs (подстановка групп regexp в line) не поддержаны в MVP — убирает класс ошибок «вписали в файл не то, что думали».
  • Запись атомарна (temp + rename), без частично-записанного файла: прерывание прогона (краш, OOM) не оставляет конфиг наполовину переписанным. При in-place правке mode/owner/group сохраняются — модуль не понижает права существующего файла молча.
  • Паттерн должен быть фиксированным якорным от автора задачи, а не из внешнего ввода. Опасно: regexp: "${ input.user_pattern }". Безопасно: regexp: '^include\s+/etc/app/legacy/'.
  • Привилегии: манифест объявляет только fs_write_root (запись за пределы /var/lib/soul-stack/), не run_as_root; правка идёт с привилегиями процесса soul-агента, для путей /etc/... на практике требует root.

См. также