Заменить строку содержимым файла

У меня есть файл A: конфигурация nginx. Где-то в этом файле у меня есть {here}

Я хочу заменить этот {here} содержимым другого файла B:

location /blah {
     proxy_pass "https://blah.com";
}

Я пробовал использовать sed и awk (gsub), но у меня возникли проблемы с возвратом/неэкранированным символом.

Я нашел решение, но работает только с шаблоном, не работает с заменой. В этом примере он добавит файл B после моего маркера вместо его замены:

sed -i '/{here}/{r fileB
:a;n;ba}' nginx.conf

person Bastien974    schedule 28.08.2014    source источник
comment
возможный дубликат Вставить текст в файл после определенного текста. Я только что ответил на этот вопрос, и он очень похож. В частности, должна работать техника awk.   -  person Tom Fenech    schedule 29.08.2014
comment
На самом деле, это немного по-другому, я все равно опубликовал ответ.   -  person Tom Fenech    schedule 29.08.2014


Ответы (3)


Это должно сделать это:

awk 'NR==FNR { a[n++]=$0; next } 
/{here}/ { for (i=0;i<n;++i) print a[i]; next }
1' fileB fileA

Создайте массив, содержащий все строки файла, содержащего замену. Когда шаблон совпадет во втором файле, распечатайте все строки. В противном случае 1 в конце означает, что печатаются исходные строки.

Как правильно заметил Эд, инициализатор цикла был неправильным. Я изменил его на i=0, чтобы печаталась первая строка. Кроме того, это заменяет все содержимое строки, содержащей {here}, что может быть или не быть тем, что вам нужно.

Чтобы перезаписать исходный файл, вы можете сделать:

awk '...' fileB fileA > tmp && mv tmp fileA
person Tom Fenech    schedule 28.08.2014
comment
Спасибо за это решение awk! Мне было бы очень интересно увидеть альтернативу sed тому, что я опубликовал. - person Bastien974; 29.08.2014
comment
Без проблем. Возможно, кто-то с лучшими навыками sed, чем я, сможет предоставить решение sed. - person Tom Fenech; 29.08.2014
comment
Это должно быть ++n вместо n++, чтобы массив начинался с 1 вместо 0, а затем цикл должен заканчиваться на <=n. Прямо сейчас он пропустит печать первой строки файла B. Это не заменяет {here} содержимым файла B, а заменяет всю строку, содержащую {here}. - person Ed Morton; 29.08.2014
comment
@ Эд, спасибо, я хотел начать цикл с 0. Я знаю, что для таких функций, как split и т. д., типично использовать индексы массива, основанные на 1, есть ли что-то неправильное в том, чтобы начинать с 0? Я предположил, что {here} находится на отдельной строке. +1 за ваше решение в любом случае. - person Tom Fenech; 29.08.2014
comment
Нет никаких технических проблем с запуском с нуля, просто это часто приводит к ошибкам, как у вас, когда вы позже забываете особый случай этого массива при циклическом просмотре его членов и начинаете его с 0 вместо 1, как обычно. Это также усложняет чтение кода для других, поскольку, когда мы видим массив, начинающийся с нуля, нам приходится немного больше думать о том, почему это должно быть, что он делает, правильно ли это делать и т. д., а не просто понимание кода с первого взгляда. Я считаю, что лучше ВСЕГДА начинать массивы awk с 1, если вам не нужно использовать массив [0] для какой-либо вспомогательной цели. - person Ed Morton; 29.08.2014
comment
@Ed Спасибо за понимание. Кроме того, интересно, что приращения постфикса достаточно для получения значения 0, т. е. awk 'BEGIN{print n++}' приводит к 0, а не к пустой строке. - person Tom Fenech; 29.08.2014
comment
Истинный. Рад, что он достаточно умен, чтобы понять, что переменная является числом из контекста, вместо того, чтобы сначала увеличивать его. Между прочим, под adjuvant purpose я подразумеваю такие вещи, как то, где вам нужно написать функцию для заполнения массива из строки (как это делает split()), и может быть полезно заполнить array[0] исходной строкой или длиной массива или чем-то другим. метаинформация отдельно от основной цели/содержимого массива. - person Ed Morton; 29.08.2014

Все что тебе нужно это:

awk 'NR==FNR{rep=(NR>1?rep RS:"") $0; next} {gsub(/{here}/,rep)}1' fileB nginx.conf

предполагая, что fileB не содержит &, поскольку они будут интерпретированы операцией замены как строка, соответствующая RE. Если это так, используйте index() и substr() в цикле.

Если {here} - ЕДИНСТВЕННАЯ вещь в строке, сообщите нам об этом, так как это немного упрощает задачу.

person Ed Morton    schedule 29.08.2014

Это может сработать для вас (GNU sed):

sed 's/\(.*\){here}\(.*\)/echo '\''\1'\''$(cat fileB)'\''\2'\''/e' fileA
person potong    schedule 28.08.2014
comment
Я пробовал, он не сохраняет символ возврата в файле B. Если я правильно понимаю, вы фиксируете то, что до и после шаблона, и повторяете их в файле B. Я немного потерялся во всех этих одиночных кавычках без экранирования. - person Bastien974; 29.08.2014