как добавить строку только если строка не установлена ​​после определенного слова

как добавить строку только если строка не установлена ​​после определенного слова

Я создаю следующий 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).

Связанный контент