Заменить маркер в текстовом файле произвольным новым текстом

У меня есть файл, который содержит текст и – в одной строке – маркер, который указывает, где следует добавлять новый контент

foo bar %SUBSTITUTE% foo 

Подменю линейной линии следует заменить новой многострочной строкой text="some text" (обратите внимание, что я не знаю строку, которая может быть, например, результатом чтения файла text= cat «file» “). Результат замены должен быть прочитан

 foo bar some text %SUBSTITUTE% foo 

У меня была рабочая версия на основе perl которая перестала работать (видимо, из-за изменения версии perl). Теперь я пытаюсь использовать стандартные утилиты, такие как tr и sed для замены строки. У меня возникают некоторые проблемы с этим, потому что строка, которую нужно вставить, может содержать произвольные символы, включая обратную косую черту и т. Д. Я не хочу избегать их перед вставкой.

Есть ли безопасный способ сделать это, который работает со стандартными инструментами? Другие вопросы, которые я нахожу для проблемы, – это конкретные решения, в которых текст, который нужно вставить, известен.

Если у вас есть версия sed, вы можете использовать команду r для чтения и вставки нового содержимого из файла, а затем удалить строку маркера, например

 sed '/%SUBSTITUTE%/{ r path/to/newcontent d }' file 

Если вы хотите сохранить маркер %SUBSTITUTE% после вставки, это сложно, потому что, что бы вы ни делали, расширение GNU r приостанавливает содержимое файла до конца текущего цикла шаблона (сохранение его раньше было бы просто вопросом удаления d команда). Вероятно, самый простой способ – добавить его в файл newcontent : вы можете сделать это на лету, как

 sed '/%SUBSTITUTE%/{ r /dev/stdin d }' file < <(sed '$a %SUBSTITUTE%' path/to/newcontent) 

Используя совершенно другой подход, вы можете разбить первый файл на %SUBSTITUTE% а затем cat в новом контенте

 csplit -s file '/%SUBSTITUTE%/' cat xx00 newcontent xx01 

Вы также можете сделать цикл read bash над строками первого файла и cat новый файл содержимого, когда вы сопоставляете строку с маркером, – однако у меня были мои костяшки, постучавшие по предложению циклов чтения для обработки текста на этом форуме. К сожалению, ни одно из них не обеспечивает решение на месте.

Вот еще один способ использования ed .
Вставьте все содержимое FILE перед маркером (т.е. перед строкой, содержащей %SUBSTITUTE% ):

 ed -s originalfile <<< $'/%SUBSTITUTE%/- r FILE\nw\nq' 

где:
/%SUBSTITUTE%/ : устанавливает адрес в первой строке, соответствующей %SUBSTITUTE%
- или -1 : смещения выходят на одну строку до
r FILE : Reads FILE to after the addressed line.
w : записывает в originalfile (заменить на ,p просто распечатать контент вместо записи)
q : редактор выходов

Замена FILE на !echo "$TEXT" будет вставлять содержимое $TEXT перед маркером:

 export TEXT ed -s originalfile <<'IN' /%SUBSTITUTE%/-1 r !echo "$TEXT" w q IN 

Я нашел следующее решение на основе awk :

 text=`cat "filepaste"` export text; <"$file" awk ' BEGIN {REPLACE=ENVIRON["text"] "\n%SUBSTITUTE%" } {gsub(/^%SUBSTITUTE%$/, REPLACE); print} ' 

Здесь «filepaste» содержит содержимое для замены %SUBSTITUTE% . Преимущество состоит в том, что эта строка может быть использована при использовании разных инструментов оболочки без необходимости сохранять их обратно в файл. Чтение переменной awk REPLACE из переменной окружения позволяет избежать расширения экранированных символов в text .

Поскольку у вас есть решение Perl, вот еще один. Я использую rep.txt для хранения замены:

 $ perl -pe '$re=`cat rep.txt`; chomp($re); s/%SUBSTITUTE%\n/$re/' file.txt foo bar multi line string foo 

Это считывает каждую строку целевого файла ( file.txt ), затем применяет к нему сценарий, заданный -e и печатает ( -p ).