Щоб уникнути змінних, які будуть використовуватися в лівій і правій частині sкоманди в sed(тут $lhsі $rhsвідповідно), слід зробити:
escaped_lhs=$(printf '%s\n' "$lhs" | sed 's:[][\/.^$*]:\\&:g')
escaped_rhs=$(printf '%s\n' "$rhs" | sed 's:[\/&]:\\&:g;$!s/$/\\/')
sed "s/$escaped_lhs/$escaped_rhs/"
Зверніть увагу, що $lhsне може містити символ нового рядка.
Тобто на LHS виходять усі оператори regexp ( ][.^$*), сам символ утечі ( \) та роздільник ( /).
На RHS вам потрібно лише втекти &, роздільник, зворотний косий риси та символ нового рядка (що ви робите, вставляючи зворотній косу риску в кінці кожного рядка, за винятком останнього ( $!s/$/\\/)).
Це передбачає, що ви використовуєте /як роздільник у своїх sed sкомандах, і ви не вмикаєте розширені RE-адреси за допомогою -r(GNU sed/ ssed/ ast/ busybox sed) або -E(BSD ast, останні GNU, нещодавно зайнятої скриньки ) або PCRE з -R( ssed) або доповненими REs з -A/ -X( ast), які всі мають додаткових операторів з РЕ.
Кілька основних правил при роботі з довільними даними:
- Не використовуйте
echo
- цитуйте свої змінні
- розглянути вплив локалі (особливо його набір символів: це важливо, щоб минути
sed команди виконуються в тій же місцевості, що і sedкоманди , використовуючи вцілілі рядки (і з тієї ж sedкомандою), наприклад)
- не забувайте про символ нового рядка (тут ви можете перевірити, чи
$lhsмістить він, і вжити заходів).
Іншим варіантом є використання perlзамість sedі передавання рядків у оточенні та використання операторів \Q/ \E perlregexp для буквального взяття рядків:
A="$lhs" B="$rhs" perl -pe 's/\Q$ENV{A}\E/$ENV{B}/g'
perl(за замовчуванням) набір символів локалі не вплине, оскільки у вищезазначеному він розглядає лише рядки як масиви байтів, не піклуючись про те, які символи (якщо такі є) вони можуть представляти для користувача. З sed, ви могли б досягти того ж, зафіксувавши локаль на Cз LC_ALL=Cдля всіх sedкоманд (хоча це також вплине на мову повідомлень про помилки, якщо такі є).