Para escapar de variáveis a serem usadas no lado esquerdo e no lado direito de um comando s
em sed
(aqui $lhs
e $rhs
respectivamente), você faria:
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/"
Observe que $lhs
não pode conter um caractere de nova linha.
Ou seja, no LHS, escape de todos os operadores regexp ( ][.^$*
), do próprio caractere de escape ( \
) e do separador ( /
).
No RHS, você só precisa escapar de &
, o separador, a barra invertida e o caractere de nova linha (o que você faz inserindo uma barra invertida no final de cada linha, exceto a última ( $!s/$/\/
)). / p>
Isso pressupõe que você use /
como separador nos comandos sed
s
e que você não habilita REs estendidos com -r
(GNU sed
/ ssed
/ ast
/ busybox sed
) ou -E
(BSDs, ast
, GNU recente, busybox recente) ou PCREs com -R
( ssed
) ou Aumentada REs com -A
/ -X
( ast
), todos com operadores RE extras.
Algumas regras básicas ao lidar com dados arbitrários:
- Não use
echo
- cite suas variáveis
- considere o impacto da localidade (especialmente seu conjunto de caracteres: é importante que os comandos escape
sed
sejam executados na mesma localidade que o comandosed
usando escapou strings (e com o mesmo comandosed
) por exemplo) - não se esqueça do caractere de nova linha (aqui você pode querer verificar se
$lhs
contém algum e executar uma ação).
Outra opção é usar perl
em vez de sed
e passar as strings no ambiente e usar os operadores \Q
/ \E
perl
regexp para obter strings literalmente:
A=lhs B=rhs perl -pe 's/\Q$ENV{A}\E/$ENV{B}/g'
perl
(por padrão) não será afetado pelo conjunto de caracteres do local, pois, acima, considera apenas as cadeias como matrizes de bytes sem se importar com quais caracteres (se houver) eles podem representar para o usuário. Com sed
, você pode conseguir o mesmo, fixando a localidade em C
com LC_ALL=C
para todos os comandos sed
(embora isso também afete o idioma das mensagens de erro, se houver).