So stellen Sie sicher, dass Zeichenfolgen, die in sed-Substitutionen interpoliert werden, allen Metazeichen entgehen

21

Ich habe ein Skript, das einen Textstrom liest und eine Datei mit sed-Befehlen generiert, die später mit ausgeführt wird sed -f. Die generierten sed Befehle sind wie folgt:

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

Angenommen, das Skript, das die sedBefehle generiert, sieht folgendermaßen aus:

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

Wie kann ich das Skript verbessern, um sicherzustellen, dass alle Regex-Metazeichen in der cidZeichenfolge ordnungsgemäß maskiert und interpoliert werden?

Dan
quelle

Antworten:

24

Um zu entkommen Variablen auf der linken Seite und die rechte Seite eines zu verwendenden sBefehls in sed(hier $lhsund $rhsrespectively), dann würden Sie tun:

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/"

Beachten Sie, dass $lhskein Newline-Zeichen enthalten sein darf.

Dies bedeutet, dass in der LHS alle regulären Ausdrücke ( ][.^$*), das Escaping-Zeichen selbst ( \) und das Trennzeichen ( /) maskiert werden .

Auf dem RHS müssen Sie nur &das Trennzeichen, den Backslash und das Newline-Zeichen (was Sie tun, indem Sie am Ende jeder Zeile einen Backslash einfügen, mit Ausnahme des letzten ( $!s/$/\\/)).

Dies setzt voraus, dass Sie /in Ihren sed sBefehlen als Trennzeichen verwenden und Extended REs nicht mit -r(GNU sed/ ssed/ ast/ busybox sed) oder -E(BSDs, astRecent GNU, Recent Busy Box) oder PCREs mit -R( ssed) oder Augmented REs mit -A/ -X( ast) aktivieren Alle haben zusätzliche RE-Operatoren.

Einige Grundregeln beim Umgang mit beliebigen Daten:

  • Nicht verwenden echo
  • Zitieren Sie Ihre Variablen
  • Berücksichtigen Sie die Auswirkung des Gebietsschemas (insbesondere seines Zeichensatzes: Es ist wichtig, dass die Escaping- sed Befehle in demselben Gebietsschema wie der sedBefehl ausgeführt werden, z. B. mit den Escaped- Zeichenfolgen (und mit demselben sedBefehl).
  • Vergessen Sie nicht das Newline-Zeichen (hier können Sie überprüfen, ob es $lhsvorhanden ist, und Maßnahmen ergreifen).

Eine weitere Möglichkeit ist die Verwendung perlstatt sedund übergeben Sie die Zeichenfolge in der Umgebung und die Verwendung \Q/ \E perlregexp Operatoren für Strings wörtlich nehmen:

A="$lhs" B="$rhs" perl -pe 's/\Q$ENV{A}\E/$ENV{B}/g'

perl(Standardmäßig) wird vom Zeichensatz des Gebietsschemas nicht beeinflusst, da oben nur die Zeichenfolgen als Bytearrays betrachtet werden, ohne darauf zu achten, welche Zeichen (falls vorhanden) sie für den Benutzer darstellen. Mit sedkönnen Sie dasselbe erreichen, indem Sie das Gebietsschema für alle Befehle auf Cmit festlegen (dies wirkt sich jedoch auch auf die Sprache der etwaigen Fehlermeldungen aus).LC_ALL=Csed

Stéphane Chazelas
quelle
Was ist, wenn ich doppelte Anführungszeichen vermeiden muss?
Menon
@Menon, doppelte Anführungszeichen sind nichts Besonderes sed, du musst ihnen nicht entkommen.
Stéphane Chazelas
Dies kann nicht für den Mustervergleich mit Platzhalterzeichen verwendet werden, oder?
Menon
@Menon, nein, Wildcard - Musterabgleich wie bei find‚s -nameist von regulären Ausdrücken anders. Dort musst du nur noch fliehen ?, *Backslash und[
Stéphane Chazelas