Вопрос или проблема
Я пытаюсь написать скрипт bash, который вставит строку после совпадения строки в
/usr/lib/systemd/system/neutron-server.service
Мне удалось сделать это на других файлах без проблем, так как я просто вставлял переменные в необходимые конфигурационные файлы, но этот файл, пох似, вызывает у меня затруднения.
Я полагаю, что ошибка в том, что sed не игнорирует специальные символы. В своей попытке я пробовал использовать одинарные и двойные кавычки в sed (что, как я понимаю, предназначено для переменных, но думал, что это может что-то изменить). Есть ли лучший способ решить эту задачу или какие-то специальные флаги или синтаксис sed, которые я упускаю?
sed ‘/--config-file /etc/neutron/plugin.ini/a\--config-file /etc/neutron/plugins/ml2/ml2_conf_cisco_apic.ini‘ /usr/lib/systemd/system/neutron-server
Кратко – вставить
--config-file /etc/neutron/plugins/ml2/ml2_conf_cisco_apic.ini
После
--config-file /etc/neutron/plugin.ini
Оригинальный файл
[Unit]
Description=OpenStack Neutron Server
After=syslog.target network.target
[Service]
Type=notify
User=neutron
ExecStart=/usr/bin/neutron-server --config-file /usr/share/neutron/neutron-
dist.conf --config-dir /usr/share/neutron/server --config-file
/etc/neutron/neutron.conf --config-file /etc/neutron/plugin.ini --config-dir
/etc/neutron/conf.d/common --config-dir /etc/neutron/conf.d/neutron-server -
-log-file /var/log/neutron/server.log
PrivateTmp=true
NotifyAccess=all
KillMode=process
TimeoutStartSec="infinity"
[Install]
WantedBy=multi-user.target
Файл после выполнения нужной команды изменения.
[Unit]
Description=OpenStack Neutron Server
After=syslog.target network.target
[Service]
Type=notify
User=neutron
ExecStart=/usr/bin/neutron-server --config-file /usr/share/neutron/neutron-
dist.conf --config-dir /usr/share/neutron/server --config-file
/etc/neutron/neutron.conf --config-file /etc/neutron/plugin.ini --config-
file /etc/neutron/plugins/ml2/ml2_conf_cisco_apic.ini --config-dir
/etc/neutron/conf.d/common --config-dir /etc/neutron/conf.d/neutron-server -
-log-file /var/log/neutron/server.log
PrivateTmp=true
NotifyAccess=all
KillMode=process
TimeoutStartSec="infinity"
[Install]
WantedBy=multi-user.target
sed -i.bak 's_--config-file /etc/neutron/plugin.ini_& --config-file /etc/neutron/plugins/ml2/ml2_conf_cisco_apic.ini_' /usr/lib/systemd/system/neutron-server.service
Оригинальный файл будет сохранен как neutron-server.service.bak
. Просто найдите строку, после которой нужно вставить что-то, и замените ее на эту строку, за которой следует вставка. Использование _
, а не /
в качестве разделителя sed
делает сегменты до и после замены гораздо менее запутанными.
Ваша проблема не в неправильной интерпретации символов sed
, а в неправильной команде a
, которую вы выбрали для применения в этой ситуации. Команда append a
вставляет текст после выбранной строки, тогда как вам нужно вставить некоторый текст внутрь выбранной строки. Для этого вам нужна команда s///
.
Если это разовое действие, то вы можете просто зафиксировать замену, как уже показал DopeGhoti. Но если вы хотите, чтобы это было “заполни и забудь”, тогда вам нужно выполнить следующие шаги:
- Определите ваши строки поиска и замены.
- Подготовьте экранированную версию этих строк, чтобы независимо от того, что они содержат, вам не нужно было снова вмешиваться в вашу команду sed.
- Запустите команду, используя экранированные строки вместо оригиналов.
# определите строки
srch="--config-file /etc/neutron/plugin.ini"
repl="--config-file /etc/neutron/plugins/ml2/ml2_conf_cisco_apic.ini"
# создайте их экранированные версии
srch_esc=$(printf '%s\n' "$srch" | sed -e 's|[][^\/.*$]|\\&|g')
repl_esc=$(printf '%s\n' "$repl" | sed -e 's|[\&/]|\\&|g;$!s/$/\\/')
# выполняем
sed -e '
/^\[Service]/,/^$/!b
/^\ExecStart=/!b
'"s/[[:space:]]${srch_esc}[[:space:]]/&${repl_esc} /"'
' yourfile
Объяснение
- Выберите соответствующий диапазон, в котором вы хотите, чтобы замена произошла, в вашем случае это строки между [Service] … пустая строка. Таким образом, мы снижаем вероятность внесения изменений в нежелательном месте.
- Оказавшись в диапазоне, мы сосредотачиваемся на строке интереса, в вашем случае это строка, начинающаяся с ExecStart=.
- Здесь мы просто используем экранированные переменные вместо оригиналов, а также выходим из одинарных кавычек, заходим в двойные кавычки … вставляем данные … выходим из двойных кавычек, возвращаемся обратно в одинарные кавычки. (чтобы все кавычки были в порядке)
Чтобы все было аккуратно, положите строку соответствия в переменную A, а строку вставки в переменную B:
$ A="--config-file /etc/neutron/plugin.ini"
$ B="--config-file /etc/neutron/plugins/ml2/ml2_conf_cisco_apic.ini"
Команда sed будет следующей:
$ sed "s/$A/& $B/" input_file
Но так как A и B содержат /, вам нужно использовать другой разделитель для sed:
$ sed "s#$A#& $B#" input_file
(& это строка соответствия)
Ответ или решение
Отвечая на ваш вопрос о том, как вставить строку после определённой строки в файле /usr/lib/systemd/system/neutron-server.service
, я рекомендую использовать команду sed
с подходящим синтаксисом. Давайте разберемся шаг за шагом.
Пробелы и Экранирование Символов
Как вы уже заметили, специальные символы в sed
могут воспрепятствовать правильному выполнению команды. В вашем случае строка, которую вы хотите вставить, содержит символы, которые могут вызвать ошибки. Чтобы избежать таких проблем, мы можем воспользоваться экранированием.
Шаги Для Вставки Строки
-
Определение Исходной и Вставляемой Строк:
Начнем с определения переменных для исходного и вставляемого значений:srch="--config-file /etc/neutron/plugin.ini" repl="--config-file /etc/neutron/plugins/ml2/ml2_conf_cisco_apic.ini"
-
Экранирование Строк:
Прежде чем использовать эти строки вsed
, мы должны экранировать специальные символы:srch_esc=$(printf '%s\n' "$srch" | sed -e 's/[\/&]/\\&/g') repl_esc=$(printf '%s\n' "$repl" | sed -e 's/[\/&]/\\&/g')
-
Использование
sed
для Выполнения Замены:
Перейдём к самомуsed
:sed -i.bak "s/$srch_esc/& $repl_esc/" /usr/lib/systemd/system/neutron-server.service
- Здесь флаг
-i.bak
создаст резервную копию файла перед изменениями. - Команда
s///
используется для замены, где&
представляет собой найденную строку.
- Здесь флаг
Пояснение Команды
- В первой части команды
s/$srch_esc/& $repl_esc/
мы ищем строку, соответствующую переменнойsrch_esc
, и заменяем её, добавляя к ней содержимое переменнойrepl_esc
. - Символ
&
в строке замены обозначает то, что было найдено, а затем к нему добавляется строка для вставки.
Заключение
Следуя вышеизложенным шагам, вы сможете корректно вставить строку в нужное место в файле neutron-server.service
. Проверяйте изменения, убедившись в правильности пути файла и содержимого, которое вы добавляете.
Если у вас возникнут какие-либо затруднения в процессе, не стесняйтесь обращаться за дополнительной помощью. Успехов в вашей работе с sed
и настройкой OpenStack Neutron!