Welche Zeichen brauche ich, um sed in einem sh-Skript zu verwenden?

248

Nimm das folgende Skript:

#!/bin/sh
sed 's/(127\.0\.1\.1)\s/\1/' [some file]

Wenn ich versuche, dies sh( dashhier) auszuführen , schlägt dies aufgrund der Klammern fehl, die maskiert werden müssen. Aber ich muss den Backslashes selbst nicht entkommen (zwischen den Oktetten oder im \soder \1). Was ist die Regel hier? Was ist, wenn ich {...}oder verwenden muss [...]? Gibt es eine Liste von dem, was ich tue und was ich nicht entkommen muss?

tückisch
quelle
1
Hier ist eine Bash-Funktion zum Konvertieren von Pfaden für die Verwendung mit SED:function sedPath { path=$((echo $1|sed -r 's/([\$\.\*\/\[\\^])/\\\1/g'|sed 's/[]]/\[]]/g')>&1) } #Escape path for use with sed
user2428118
Dura lex, sed sed
Nemo

Antworten:

281

Hier gibt es zwei Interpretationsebenen: die Shell und sed.

In der Shell wird alles zwischen einfachen Anführungszeichen wörtlich interpretiert, mit Ausnahme von einfachen Anführungszeichen. Sie können effektiv ein einfaches Anführungszeichen zwischen einfachen Anführungszeichen setzen, indem Sie schreiben '\''( einfaches Anführungszeichen schließen, einfaches Anführungszeichen wörtlich, einfaches Anführungszeichen öffnen).

Sed verwendet grundlegende reguläre Ausdrücke . In einer BRE müssen die Zeichen $.*[\^in Anführungszeichen gesetzt werden, damit sie buchstäblich behandelt werden, mit Ausnahme der in Zeichensätzen ( […]) enthaltenen Schrägstriche . Buchstaben, Ziffern und (){}+?|dürfen nicht in Anführungszeichen gesetzt werden (einige davon können in einigen Implementierungen nicht in Anführungszeichen gesetzt werden). Die Sequenzen \(, \), \n, und in einigen Implementierungen \{, \}, \+, \?, \|und andere Backslash + alphanumerics haben eine besondere Bedeutung. $^In einigen Implementierungen kann es passieren, dass Sie in einigen Positionen nicht zitieren .

Darüber hinaus benötigen Sie einen Backslash, bevor /er im regulären Ausdruck außerhalb von Klammern angezeigt werden soll. Sie können ein alternatives Zeichen als Trennzeichen auswählen, indem Sie schreiben, z. B. s~/dir~/replacement~oder \~/dir~p; Sie benötigen einen Backslash vor dem Begrenzer, wenn Sie ihn in die BRE aufnehmen möchten. Wenn Sie ein Zeichen auswählen, das in einer BRE eine besondere Bedeutung hat, und es buchstäblich einschließen möchten, benötigen Sie drei umgekehrte Schrägstriche. Ich empfehle dies nicht, da es sich in einigen Implementierungen anders verhalten kann.

Kurz gesagt, für sed 's/…/…/':

  • Schreiben Sie den regulären Ausdruck in einfache Anführungszeichen.
  • Verwenden Sie '\''diese Option, um ein einfaches Anführungszeichen im regulären Ausdruck zu erhalten.
  • Setzen Sie einen Backslash vor $.*/[\]^und nur diese Zeichen (aber nicht in Klammern). (Technisch gesehen sollte man nicht einen umgekehrten Schrägstrich setzen , bevor ]aber ich weiß nicht von einer Implementierung , die behandelt ]und \]außerhalb der Klammerausdrücke anders.)
  • -Um buchstäblich behandelt zu werden, stellen Sie in einem Klammerausdruck sicher, dass er der erste oder letzte ist ( [abc-]oder [-abc]nicht [a-bc]).
  • ^Um buchstäblich behandelt zu werden, stellen Sie in einem Klammerausdruck sicher, dass er nicht an erster Stelle steht (use [abc^], not [^abc]).
  • Wenn Sie ]in die Liste der Zeichen aufnehmen möchten, die mit einem Klammerausdruck übereinstimmen, geben Sie als erstes Zeichen (oder als erstes nach ^einem negierten Satz) Folgendes an: []abc]oder [^]abc](weder [abc]]noch[abc\]] ).

Im Ersetzungstext:

  • &und \müssen in Anführungszeichen gesetzt werden, ebenso wie das Trennzeichen (normalerweise /) und die Zeilenumbrüche.
  • \gefolgt von einer Ziffer hat eine besondere Bedeutung. \gefolgt von einem Buchstaben hat in einigen Implementierungen eine spezielle Bedeutung (Sonderzeichen), \gefolgt von einem anderen Zeichen \coder cabhängig von der Implementierung.
  • sed 's/…/…/'Verwenden Sie einfache Anführungszeichen um das Argument ( ), '\''um ein einfaches Anführungszeichen in den Ersetzungstext einzufügen.

Wenn der reguläre Ausdruck oder der Ersetzungstext aus einer Shell-Variablen stammt, denken Sie daran

  • Der reguläre Ausdruck ist eine BRE, keine wörtliche Zeichenfolge.
  • In der regulären Ausdrucksweise muss eine neue Zeile wie folgt ausgedrückt werden \n(was niemals zutrifft, es sei denn, Sie haben einen anderen sedCode, der dem Musterraum Zeichen für neue Zeilen hinzufügt). Beachten Sie jedoch, dass dies bei einigen sedImplementierungen nicht in Klammern funktioniert .
  • Im Ersetzungstext müssen &, \und Zeilenumbrüche zitiert werden.
  • Das Trennzeichen muss in Anführungszeichen gesetzt werden (jedoch nicht in Klammern).
  • Verwenden Sie doppelte Anführungszeichen für die Interpolation: sed -e "s/$BRE/$REPL/".
Gilles
quelle
Wenn Sie das eigentliche Platzhalterzeichen (*) maskieren, können Sie einen doppelten Backslash ( \\*) verwenden. Beispiel:echo "***NEW***" | sed /\\*\\*\\*NEW\\*\\*\\*/s/^/#/
danger89
43

Das Problem, das Sie haben, liegt nicht an der Shell-Interpolation und an der Escape-Funktion, sondern daran, dass Sie versuchen, die Syntax für erweiterte reguläre Ausdrücke zu verwenden, ohne die Option -roder zu übergeben --regexp-extended.

Ändern Sie Ihre Sed-Linie von

sed 's/(127\.0\.1\.1)\s/\1/' [some file]

zu

sed -r 's/(127\.0\.1\.1)\s/\1/' [some file]

und es wird funktionieren, wie ich glaube, dass Sie beabsichtigen.

Standardmäßig verwendet sed uses grundlegende reguläre Ausdrücke (think grep style), für die die folgende Syntax erforderlich wäre:

sed 's/\(127\.0\.1\.1\)[ \t]/\1/' [some file]
R Perrin
quelle
Ich hatte dieses Problem erneut und vergaß, nach unten zu scrollen, um die Lösung zu finden, die ich beim letzten Mal gefunden hatte. Danke noch einmal.
Isaaclw
Danke vielmals. Das Hinzufügen -rals Option war in meinem Fall erforderlich.
HelloGoodbye
15

Verwenden Sie einfache Anführungszeichen für den gesamten Ausdruck, es sei denn, Sie möchten eine Shell-Variable in den sed-Ausdruck interpolieren, da sie bewirken, dass alles zwischen ihnen so interpretiert wird, wie es ist, einschließlich der umgekehrten Schrägstriche.

Wenn Sie also möchten, dass sed s/\(127\.0\.1\.1\)\s/\1/einfache Anführungszeichen sieht , und die Shell die Klammern oder umgekehrten Schrägstriche darin nicht berührt. Wenn Sie eine Shell-Variable interpolieren müssen, setzen Sie nur diesen Teil in doppelte Anführungszeichen. Z.B

sed 's/\(127\.0\.1\.1\)/'"$ip"'/'

Dadurch sparen Sie sich die Mühe, sich zu merken, welche Shell-Metazeichen nicht durch doppelte Anführungszeichen geschützt sind.

Kyle Jones
quelle
Ich möchte sedsehen s/(127\.0\.1\.1)/..., aber das in ein Shell-Skript so wie es ist zu setzen, funktioniert nicht. Was Sie über die Shell sagen, die die Klammern nicht berührt, scheint falsch zu sein. Ich habe meine Frage zum Ausarbeiten bearbeitet.
Detly
3
Die Shell berührt die Klammern nicht. Du brauchst die Backslases, weil sed sie sehen muss. sed 's/(127\.0\.1\.1)/IP \1/'schlägt fehl, weil sed \(und \)für die Gruppensyntax sehen muss, nicht (und ).
Kyle Jones
facepalm Es ist nicht in der Manpage, aber es ist in einem Online-Handbuch, das ich gefunden habe. Ist das normal für Regex, weil ich es nie in Regex-Bibliotheken (in zB Python) verwenden musste?
Detly
3
Für traditionelle Unix-Befehle gibt es grundlegende reguläre Ausdrücke und erweiterte reguläre Ausdrücke. Einzelheiten . sed verwendet grundlegende reguläre Ausdrücke, daher werden die Backslashes für die Gruppensyntax benötigt. Perl und Python gingen sogar über erweiterte reguläre Ausdrücke hinaus. Während ich herumstocherte, fand ich eine äußerst informative Tabelle , die zeigt, was für eine verwirrende Brombeere wir zaubern, wenn wir glanzvoll "regulären Ausdruck" sagen.
Kyle Jones
1
Ich möchte auch hinzufügen, dass das einzige Zeichen, das in einfachen Anführungszeichen nicht verwendet werden kann, ein einfaches Anführungszeichen ist.
Enzotib