2016-02-26 2 views
1

У меня есть файл конфигурации, который состоит из нескольких разделов, и многие из них содержат свойство с тем же именем, пусть оно будет host. Мне нужно заменить свойство host только одного конкретного раздела. Вот файл:Многострочный шаблон после сопоставления с использованием sed

section1 { 
    ... 
    setting1 = "true" 
    ... 
    host = "localhost" 
    ... 
} 
section2 { 
    ... 
    host = "whatever" 
    ... 
} 

Я хочу, чтобы заменить значение host в section2 с чем-то еще. Обратите внимание, что может быть любое количество строк между ними, помеченный как ...

ответ

2
sed -i.bak '/^section2 {/,/^}/s/host .*/host = "newvalue"/' file 

Это будет искать между section2 { и следующей }, изменяя все host = вхождений. Синтаксис GNU sed, вы должны использовать, например, sed -i '' ... на OSX.

+0

Мне также пришлось добавить '-i', но да, это сработало отлично. Спасибо! –

1

awk на помощь!

$ awk 'BEGIN{RS=ORS="\n}"} 
    /section2/{sub("host =[^\n]*", "host = \"newvalue\"")}NF' file 

section1 { 
    ... 
    setting1 = "true" 
    ... 
    host = "localhost" 
    ... 
} 
section2 { 
    ... 
    host = "newvalue" 
    ... 
} 

определите структуру записи, найдите соответствующую запись и замените ее.

+1

Перспективный, но (а) он печатает дополнительный '\ n}' в конце, потому что конечный '\ n' интерпретируется как (пустая) последняя запись и (b) заменяет все, начиная с' host = ' _ через конец записи_, потенциально уничтожая другие ключи. Требуется GNU Awk или Mawk из-за использования многосимвольного 'RS'. – mklement0

+0

Да, спасибо, я этого раньше не замечал, нужно удалить лишнюю скобу. Я предполагаю, что ваш второй комментарий должен быть в порядке, основываясь на макете файла. – karakfa

+1

Спасибо за обновление (более короткий вариант - заменить '1' на' NF' ('NF> 0')); 2-й комментарий: OP говорит: «Обратите внимание, что между ними может быть любое количество строк, помеченных как« ... », поэтому я считаю справедливым предположить, что строки после строки' host' _do_ необходимо сохранить. – mklement0

0

Joao Morais's answer, вероятно, лучший выбор.

Чтобы дополнить его с awk раствором (исправленное, более надежная версия karakfa's answer):

awk ' 
    BEGIN{ RS=ORS="}\n" } 
    /^section2/ { $0 =gensub(/(\n *host =)[^\n]*/, "\\1\"new value\"", 1) } 
    1' file 
  • С mawk (менее надежный; также работает с GNU awk):
awk ' 
    BEGIN{ RS=ORS="}\n" } 
    /^section2/ { sub(/ host = [^\n]*/, " host = \"new value\"") } 
    1' file 

Примечание: В связи с использованием значения с множеством символов RS, ни решение работает с BSD/OSX awk; заставляя его работать, потребовалось бы больше усилий.

Пояснение:

  • RS=ORS="}\n" говорит awk разделить вклад в записи с помощью }\n экземпляров (с помощью специальной переменной RS, входной разделитель записей), а также использовать один и тот же разделитель для вывода (с помощью ORS, разделитель выходной записи).

  • /^section2/ спичка буквальной section на начале каждой записи.

    • Mawk решение: { sub(/ host = [^\n]*/, " host = \"new value\"") } заменяет значение ключа host с новым значением.

      • Обратите внимание на необходимости соответствовать только к следующей новой строке ([^\n]*), потому что только .* будет соответствовать к концу записи, через разрывы строк.
      • Было бы предпочтительнее, чтобы соответствовать ключу host более надежно, например, с
        sub("/(\n *host =)...), но вы должны были бы ссылки снимаемого группы, чтобы сохранить такое же количество предшествующих пробельных в восстановительной стоимости, которая sub() не поддержка.
        GNUawk, однако, предлагает ссылки на группы захвата через нестандартную функцию gensub(); Смотри ниже.
    • GNU Awk решение: Использование нестандартного gensub() функции позволяет использовать группы захвата (\\1 относится к 1-й группе захвачена, ...), что делает замену как более надежной и более удобной.

      • gensub(), в отличие от sub() не изменяет входной строки и вместо возвращается измененная копия - следовательно, необходимо присвоить его $0.
      • 1 как третий аргумент сообщает gensub() только для замены появление.
  • 1 является общим сокращением для печати просто результирующую запись.

+1

Спасибо, @EdMorton; Я изменил ответ и включил (измененную версию) вашу команду. – mklement0

Смежные вопросы