Экранирование строки для шаблона замены sed
Вопрос
В моем сценарии bash у меня есть внешняя (полученная от пользователя) строка, которую я должен использовать в шаблоне sed.
REPLACE="<funny characters here>"
sed "s/KEYWORD/$REPLACE/g"
Как я могу избежать $REPLACE
строка, чтобы она была безопасно принята sed
в качестве буквальной замены?
ПРИМЕЧАНИЕ: А KEYWORD
это тупая подстрока без совпадений и т. д.Он не предоставляется пользователем.
Решение
Предупреждение:Это делает нет рассмотрите новые строки.Более подробный ответ см. этот SO-вопрос вместо.(Спасибо, Эд Мортон и Никлас Питер)
Обратите внимание, что избегать всего — плохая идея.Седу нужно, чтобы много символов было экранировано. получать их особое значение.Например, если вы экранируете цифру в строке замены, она превратится в обратную ссылку.
Как сказал Бен Бланк, в строке замены необходимо экранировать только три символа (сами экранируются, косая черта для конца оператора и & для замены всех):
sed -e 's/[\/&]/\\&/g'
Если вам когда-нибудь понадобится сбежать из KEYWORD
строка, вам нужна следующая:
sed -e 's/[]\/$*.^[]/\\&/g'
Помните, что если вы используете символ, отличный от /
в качестве разделителя вам нужно заменить косую черту в приведенных выше выражениях на используемый вами символ.См. комментарий PeterJCLaw для объяснения.
Отредактировано: Из-за некоторых необычных случаев, которые ранее не учитывались, приведенные выше команды менялись несколько раз.Подробности смотрите в истории изменений.
Другие советы
Команда sed позволяет использовать другие символы вместо /
в качестве разделителя:
sed 's#"http://www\.fubar\.com"#URL_FUBAR#g'
Двойные кавычки не являются проблемой.
Единственные три буквальных символа, которые обрабатываются особым образом в предложении замены, — это /
(чтобы закрыть пункт), \
(чтобы экранировать символы, обратную ссылку и т. д.) и &
(чтобы включить совпадение в замену).Поэтому все, что вам нужно сделать, это экранировать эти три символа:
sed "s/KEYWORD/$(echo $REPLACE | sed -e 's/\\/\\\\/g; s/\//\\\//g; s/&/\\\&/g')/g"
Пример:
$ export REPLACE="'\"|\\/><&!"
$ echo fooKEYWORDbar | sed "s/KEYWORD/$(echo $REPLACE | sed -e 's/\\/\\\\/g; s/\//\\\//g; s/&/\\\&/g')/g"
foo'"|\/><&!bar
На основе регулярных выражений Pianosaurus я создал функцию bash, которая экранирует как ключевое слово, так и замену.
function sedeasy {
sed -i "s/$(echo $1 | sed -e 's/\([[\/.*]\|\]\)/\\&/g')/$(echo $2 | sed -e 's/[\/&]/\\&/g')/g" $3
}
Вот как вы его используете:
sedeasy "include /etc/nginx/conf.d/*" "include /apps/*/conf/nginx.conf" /etc/nginx/nginx.conf
Немного поздно отвечать...но есть гораздо более простой способ сделать это.Просто измените разделитель (т. е. символ, разделяющий поля).Итак, вместо s/foo/bar/
ты пишешь s|bar|foo
.
И вот простой способ сделать это:
sed 's|/\*!50017 DEFINER=`snafu`@`localhost`\*/||g'
Полученный результат лишен этого неприятного предложения DEFINER.
Оказывается, вы задаете неправильный вопрос.Я тоже задал неправильный вопрос.Причиной неправильности является начало первого предложения:"В моем бить сценарий...".
У меня был тот же вопрос и я сделал ту же ошибку.Если вы используете bash, вам не нужно использовать sed для замены строк (и это много очиститель, чтобы использовать функцию замены, встроенную в bash).
Вместо чего-то вроде:
function escape-all-funny-characters() { UNKNOWN_CODE_THAT_ANSWERS_THE_QUESTION_YOU_ASKED; }
INPUT='some long string with KEYWORD that need replacing KEYWORD.'
A="$(escape-all-funny-characters 'KEYWORD')"
B="$(escape-all-funny-characters '<funny characters here>')"
OUTPUT="$(sed "s/$A/$B/g" <<<"$INPUT")"
вы можете использовать исключительно функции bash:
INPUT='some long string with KEYWORD that need replacing KEYWORD.'
A='KEYWORD'
B='<funny characters here>'
OUTPUT="${INPUT//"$A"/"$B"}"
Используйте awk — это чище:
$ awk -v R='//addr:\\file' '{ sub("THIS", R, $0); print $0 }' <<< "http://file:\_THIS_/path/to/a/file\\is\\\a\\ nightmare"
http://file:\_//addr:\file_/path/to/a/file\\is\\\a\\ nightmare
Вот пример AWK, который я использовал некоторое время назад.Это AWK, который печатает новые AWKS.Поскольку AWK и SED схожи, это может быть хорошим шаблоном.
ls | awk '{ print "awk " "'"'"'" " {print $1,$2,$3} " "'"'"'" " " $1 ".old_ext > " $1 ".new_ext" }' > for_the_birds
Это выглядит чрезмерным, но каким-то образом эта комбинация кавычек позволяет сохранить ' как литералы.Тогда, если я правильно помню, доступные просто заключаются в такие кавычки:«1 доллар».Попробуйте, дайте мне знать, как это работает с SED.
У меня есть улучшение по сравнению с функцией sedeasy, которая БУДЕТ ломаться со специальными символами, такими как табуляция.
function sedeasy_improved {
sed -i "s/$(
echo "$1" | sed -e 's/\([[\/.*]\|\]\)/\\&/g'
| sed -e 's:\t:\\t:g'
)/$(
echo "$2" | sed -e 's/[\/&]/\\&/g'
| sed -e 's:\t:\\t:g'
)/g" "$3"
}
Итак, что же отличается? $1
и $2
заключены в кавычки, чтобы избежать расширения оболочки и сохранить табуляцию или двойные пробелы.
Дополнительные трубопроводы | sed -e 's:\t:\\t:g'
(Мне нравится :
как токен), который преобразует вкладку в \t
.
не забывайте обо всех удовольствиях, которые возникают при ограничении оболочки вокруг " и '
итак (в кш)
Var=">New version of \"content' here <"
printf "%s" "${Var}" | sed "s/[&\/\\\\*\\"']/\\&/g' | read -r EscVar
echo "Here is your \"text\" to change" | sed "s/text/${EscVar}/g"
Если случается так, что вы генерируете случайный пароль для перехода к sed
заменить шаблон, тогда вам следует внимательно выбирать набор символов в случайной строке.Если вы выберете пароль, созданный путем кодирования значения в формате Base64, то будет только один символ, который возможен как в Base64, так и в качестве специального символа в sed
заменить шаблон.Этот символ — «/» и его легко удалить из генерируемого пароля:
# password 32 characters log, minus any copies of the "/" character.
pass=`openssl rand -base64 32 | sed -e 's/\///g'`;
Если вы просто хотите заменить значение переменной в команде SED, просто удалите пример:
sed -i 's/dev-/dev-$ENV/g' test to sed -i s/dev-/dev-$ENV/g test
Более простой способ сделать это — заранее создать строку и использовать ее в качестве параметра для sed
rpstring="s/KEYWORD/$REPLACE/g"
sed -i $rpstring test.txt