Ich habe ein Skript, das einen Textstrom liest und eine Datei mit sed-Befehlen generiert, die später mit sed -f
ausgeführt wird . Die generierten sed-Befehle sind wie folgt:
s/cid:[email protected]/https://mysite.com/files/1922/g
s/cid:[email protected]/https://mysite.com/files/1923/g
s/cid:[email protected]/https://mysite.com/files/1924/g
Nehmen Sie das Skript an, das den sed
generiert Befehle ist etwas wie:
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 alle Regex-Metazeichen in der cid
sicherzustellen string werden korrekt maskiert und interpoliert?
Akzeptierte Antwort:
Escape-Variablen, die auf der linken und rechten Seite eines s
verwendet werden Befehl in sed
(hier $lhs
und $rhs
bzw.), würden Sie Folgendes tun:
escaped_lhs=$(printf '%sn' "$lhs" | sed 's:[][\/.^$*]:\&:g')
escaped_rhs=$(printf '%sn' "$rhs" | sed 's:[\/&]:\&:g;$!s/$/\/')
sed "s/$escaped_lhs/$escaped_rhs/"
Beachten Sie, dass $lhs
darf kein Zeilenumbruchzeichen enthalten.
Das heißt, auf der LHS alle Regexp-Operatoren maskieren (][.^$*
), das Escapezeichen selbst ( ) und das Trennzeichen (
/
).
Auf der rechten Seite müssen Sie nur &
maskieren , das Trennzeichen, den umgekehrten Schrägstrich und das Zeilenumbruchzeichen (was Sie tun, indem Sie am Ende jeder Zeile außer der letzten einen umgekehrten Schrägstrich einfügen ($!s/$/\/
)).
Das setzt voraus, dass Sie /
verwenden als Trennzeichen in Ihrem sed
s
Befehle und dass Sie Erweiterte REs nicht aktivieren mit -r
(GNU sed
/ssed
/ast
/busybox sed
) oder -E
(BSDs, ast
, aktuelles GNU, aktuelles busybox) oder PCREs mit -R
(ssed
) oder Erweiterte REs mit -A
/-X
(ast
), die alle über zusätzliche RE-Operatoren verfügen.
Ein paar Grundregeln für den Umgang mit beliebigen Daten:
- Verwende
echo
nicht - zitieren Sie Ihre Variablen
- Berücksichtigen Sie die Auswirkungen des Gebietsschemas (insbesondere seines Zeichensatzes:Es ist wichtig, dass die Escapezeichen
sed
Befehle werden im gleichen Gebietsschema wiesed
ausgeführt Befehl mit dem escaped Strings (und mit demselbensed
Befehl) zum Beispiel) - Vergessen Sie nicht das Zeilenumbruchzeichen (hier sollten Sie prüfen, ob
$lhs
enthält und Maßnahmen ergreifen).
Eine andere Möglichkeit ist die Verwendung von perl
statt sed
und übergeben Sie die Zeichenfolgen in der Umgebung und verwenden Sie das Q
/E
perl
Regexp-Operatoren, um Strings wörtlich zu nehmen:
A="$lhs" B="$rhs" perl -pe 's/Q$ENV{A}E/$ENV{B}/g'
perl
(standardmäßig) wird nicht vom Zeichensatz des Gebietsschemas beeinflusst, da es oben die Zeichenfolgen nur als Arrays von Bytes betrachtet, ohne sich darum zu kümmern, welche Zeichen (falls vorhanden) sie für den Benutzer darstellen. Mit sed
, könnten Sie dasselbe erreichen, indem Sie das Gebietsschema auf C
festlegen mit LC_ALL=C
für alle sed
Befehle (obwohl dies auch die Sprache von Fehlermeldungen beeinflusst, falls vorhanden).