Aby uniknąć zmiennych, które będą używane po lewej i prawej stronie s
polecenia w sed
( odpowiednio $lhs
i tutaj $rhs
), należy:
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/"
Uwaga: $lhs
nie może zawierać znaku nowej linii.
Oznacza to, że w LHS unikaj wszystkich operatorów wyrażeń regularnych ( ][.^$*
), samego znaku ucieczki ( \
) i separatora ( /
).
W RHS wystarczy tylko uciec &
, separator, odwrotny ukośnik i znak nowej linii (co robisz, wstawiając odwrotny ukośnik na końcu każdego wiersza z wyjątkiem ostatniego ( $!s/$/\\/
)).
Zakłada się, że używasz /
jako separatora w swoich sed
s
poleceniach i że nie włączasz rozszerzonych RE z -r
(GNU sed
/ ssed
/ ast
/ busybox sed
) lub -E
(BSD ast
, ostatnio GNU, ostatnio zajęty ) lub PCRE z -R
( ssed
) lub rozszerzonych RE z -A
/ -X
( ast
), które wszyscy mają dodatkowych operatorów RE.
Kilka podstawowych zasad dotyczących arbitralnych danych:
- Nie używaj
echo
- podaj swoje zmienne
- rozważ wpływ ustawienia narodowego (zwłaszcza jego zestawu znaków: ważne jest, aby polecenia ucieczkowe
sed
były uruchamiane w tym samym języku, co sed
polecenie, na przykład za pomocą znaków ucieczki (i tej samej sed
komendy))
- nie zapomnij o znaku nowej linii (tutaj możesz sprawdzić, czy
$lhs
zawiera jakiś znak i podjąć działania).
Inną opcją jest użycie perl
zamiast sed
i przekazanie ciągów w środowisku oraz użycie operatorów \Q
/ \E
perl
regexp do dosłownego pobierania ciągów:
A="$lhs" B="$rhs" perl -pe 's/\Q$ENV{A}\E/$ENV{B}/g'
perl
(domyślnie) zestaw znaków ustawień regionalnych nie będzie miał na to wpływu, ponieważ powyżej traktuje ciągi jako tablice bajtów bez dbania o to, jakie znaki (jeśli występują) mogą reprezentować dla użytkownika. Dzięki sed
, można osiągnąć to samo poprzez ustalenie locale aby C
ze LC_ALL=C
wszystkich sed
komend (mimo że wpłynie także na język komunikatów o błędach, jeśli w ogóle).