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

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

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

foo
bar
%SUBSTITUTE%
foo

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

foo
bar
some text
%SUBSTITUTE%
foo

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

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

решение1

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

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

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

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

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

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

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

решение2

Вот еще один способ использования 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

решение3

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

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

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

решение4

Поскольку у вас было решение на 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).

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