Я создаю следующий sed cli, чтобы добавить следующую строку
force_https_protocol=PROTOCOL_TLSv1_2
после строки:
[security]
sed cli:
sed -i '/\[security\]/a force_https_protocol=PROTOCOL_TLSv1_2' /etc/ambari-agent/conf/ambari-agent.ini
но проблема в том, что когда линия force_https_protocol=PROTOCOL_TLSv1_2
уже после[security]
как изменить синтаксис sed, чтобы игнорировать добавление строки, если она уже существует?
ожидаемый результат
[security]
force_https_protocol=PROTOCOL_TLSv1_2
keysdir=/var/lib/ambari-agent/keys
server_crt=ca.crt
passphrase_env_var_name=AMBARI_PASSPHRASE
неправильный файл:
[security]
force_https_protocol=PROTOCOL_TLSv1_2
force_https_protocol=PROTOCOL_TLSv1_2
keysdir=/var/lib/ambari-agent/keys
server_crt=ca.crt
passphrase_env_var_name=AMBARI_PASSPHRASE
решение1
Предполагая, что все «force_https_protocol=PROTOCOL_TLSv1_2» в файле находятся внутри тега [security] и не появляются в других местах
sed -e '/^force_https_protocol=PROTOCOL_TLSv1_2$/d'\
-e '/\[security\]/a force_https_protocol=PROTOCOL_TLSv1_2' file
Идея состоит в том, чтобы удалить все строки в файле, содержащие (только) строку «force_https_protocol=PROTOCOL_TLSv1_2».
Объяснение
/^force_https_protocol=PROTOCOL_TLSv1_2$/d'
Удалите все строки, содержащие строку «force_https_protocol=PROTOCOL_TLSv1_2».
Тесты
$ cat file
[security]
force_https_protocol=PROTOCOL_TLSv1_2
keysdir=/var/lib/ambari-agent/keys
server_crt=ca.crt
passphrase_env_var_name=AMBARI_PASSPHRASE
[security]
keysdir=/var/lib/ambari-agent/keys
server_crt=ca.crt
passphrase_env_var_name=AMBARI_PASSPHRASE
$ sed -e '/^force_https_protocol=PROTOCOL_TLSv1_2$/d'\
-e '/\[security\]/a force_https_protocol=PROTOCOL_TLSv1_2' file
[security]
force_https_protocol=PROTOCOL_TLSv1_2
keysdir=/var/lib/ambari-agent/keys
server_crt=ca.crt
passphrase_env_var_name=AMBARI_PASSPHRASE
[security]
force_https_protocol=PROTOCOL_TLSv1_2
keysdir=/var/lib/ambari-agent/keys
server_crt=ca.crt
passphrase_env_var_name=AMBARI_PASSPHRASE
решение2
1. Использование AWK
Вот возможное решение более общей проблемы добавлениянитьк помеченному блоку текста (т. е. строкам текста, которые следуют за определенной меткой в файле и предшествуют следующей метке или номеру файла) только еслинитьпока отсутствует.
Простой подход — пройтись по файлу в обратном направлении, отследить последний раз, когда мы с ним столкнулись.нить, печатаем его каждый раз, когда сталкиваемся с определенной этикеткой (непосредственно перед печатью самой этикетки), но только если мы ее не видели (нить) пока, и забудьте о том, что вы с этим столкнулись (нить) всякий раз, когда мы сталкиваемся с меткой. И, конечно же, отменяем результат на последнем шаге.
tac file | awk '
/^force_https_protocol=PROTOCOL_TLSv1_2$/ {
seen = 1
}
/^\[security\]$/ {
if ( ! seen ) {
print "force_https_protocol=PROTOCOL_TLSv1_2"
}
}
/^\[[^]]*\]$/ {
seen = 0
}
1' | tac
tac
, включен вGNU Coreutils, объединяет и реверсирует файлы построчно. Как реверсировать файл с помощью других/стандартных инструментов, рассматривается в нескольких вопросах/ответах на U&L (например,Как команда sed '1!G;h;$!d' обращает содержимое файла?иОбратный грэппинг).
Это не будет вставлено force_https_protocol=PROTOCOL_TLSv1_2
после первого [security]
в следующем фрагменте текста:
[security]
keysdir=/var/lib/ambari-agent/keys
force_https_protocol=PROTOCOL_TLSv1_2
passphrase_env_var_name=AMBARI_PASSPHRASE
[security]
...
Чтобы просто вставитьнитьпосле каждого появления метки, если тольконитьуже существует, тот же подход может привести к:
tac file | awk '
/^force_https_protocol=PROTOCOL_TLSv1_2$/ {
seen = NR
}
/^\[security\]$/ {
if ( ( NR == 1 ) || ! ( NR == seen + 1 ) ) {
print "force_https_protocol=PROTOCOL_TLSv1_2"
}
}
1' | tac
(Этотволявставить force_https_protocol=PROTOCOL_TLSv1_2
после [security]
в приведенном выше текстовом примере).
2. Использование sed
С помощью этого цикла sed
вы можете использовать вариацию N;P;D;
(которая подробно описана в других вопросах и ответах на сайте U&L, например):Как использовать sed для замены многострочной строки?илиКак редактировать следующую строку после шаблона с помощью sed?).
Это вставит строку force_https_protocol=PROTOCOL_TLSv1_2
после строки, состоящей исключительно из [security]
только, если первая еще не там.
sed '
$! {
N
P
/^\[security\]\n/ {
/\nforce_https_protocol=PROTOCOL_TLSv1_2$/ b b
i\
force_https_protocol=PROTOCOL_TLSv1_2
}
:b
D
}
s/^\[security\]$/&\
force_https_protocol=PROTOCOL_TLSv1_2/'
Этот скрипт:
- для каждой строки, за исключением последней (
$!
), добавляет новую строку, за которой следует следующая строка, в область шаблона (N
) (которая уже содержит текущую строку); - печатает первую строку в пространстве шаблона (
P
); если эта строка[security]
...- ...а следующая строка —
force_https_protocol=PROTOCOL_TLSv1_2
просто удаляет первую строку из пространства шаблонов и начинает новый цикл (b b
,:b D
); - в противном случае печатает
force_https_protocol=PROTOCOL_TLSv1_2
на стандартном выводе (i\
) перед удалением первой строки из пространства шаблона и началом нового цикла (D
);
- ...а следующая строка —
- если последняя строка —
[security]
, заменяет ее собой, за которой следует новая строка иforce_https_protocol=PROTOCOL_TLSv1_2
и выводит результат (с помощью подразумеваемогоp
).