
У меня есть файл, содержащий текст и — в одной строке — маркер, который указывает, куда следует добавить новый контент.
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
).