Jak upewnić się, że łańcuch interpolowany do podstawienia `sed` ucieka przed wszystkimi metazarami


21

Mam skrypt, który odczytuje strumień tekstowy i generuje plik poleceń sed, który jest następnie uruchamiany sed -f. Wygenerowane polecenia sed są następujące:

s/cid:image002\.gif@01CC3D46\.926E77E0/https:\/\/mysite.com\/files\/1922/g
s/cid:image003\.gif@01CC3D46\.926E77E0/https:\/\/mysite.com\/files\/1923/g
s/cid:image004\.jpg@01CC3D46\.926E77E0/https:\/\/mysite.com\/files\/1924/g

Załóżmy, że skrypt generujący sedpolecenia jest podobny do:

while read cid fileid
do
    cidpat="$(echo $cid | sed -e s/\\./\\\\./g)"
    echo 's/'"$cidpat"'/https:\/\/mysite.com\/files\/'"$fileid"'/g' >> sedscr
done

Jak mogę ulepszyć skrypt, aby mieć pewność, że wszystkie metaznaki wyrażenia regularnego w cidciągu są odpowiednio zmieniane i interpretowane?

Odpowiedzi:


24

Aby uniknąć zmiennych, które będą używane po lewej i prawej stronie spolecenia w sed( odpowiednio $lhsi 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: $lhsnie 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 spoleceniach 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 sedpolecenie, na przykład za pomocą znaków ucieczki (i tej samej sedkomendy))
  • nie zapomnij o znaku nowej linii (tutaj możesz sprawdzić, czy $lhszawiera jakiś znak i podjąć działania).

Inną opcją jest użycie perlzamiast sedi przekazanie ciągów w środowisku oraz użycie operatorów \Q/ \E perlregexp 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 Cze LC_ALL=Cwszystkich sedkomend (mimo że wpłynie także na język komunikatów o błędach, jeśli w ogóle).


Co jeśli muszę uciec przed podwójnymi cudzysłowami?
Menon

@ Menon, podwójne cudzysłowy nie są wyjątkowe sed, nie musisz przed nimi uciekać.
Stéphane Chazelas

Nie można tego użyć do dopasowania wzorców za pomocą symboli wieloznacznych, prawda?
Menon

@Menon, nie, dopasowanie wzoru wieloznacznego, podobnie jak w find's, -nameróżni się od wyrażeń regularnych. Tam musisz tylko uciec ?, *[
odwrócić
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.