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
echonicht - zitieren Sie Ihre Variablen
- Berücksichtigen Sie die Auswirkungen des Gebietsschemas (insbesondere seines Zeichensatzes:Es ist wichtig, dass die Escapezeichen
sedBefehle werden im gleichen Gebietsschema wiesedausgeführt Befehl mit dem escaped Strings (und mit demselbensedBefehl) zum Beispiel) - Vergessen Sie nicht das Zeilenumbruchzeichen (hier sollten Sie prüfen, ob
$lhsenthä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).