So übergeben Sie eine Variable mit Schrägstrichen an sed

100

Wie übergibt man eine Variable mit Schrägstrichen als Muster sed?

Zum Beispiel, wenn ich die folgende Variable habe:

var="/Users/Documents/name/file"

Ich möchte es sedso weitergeben:

sed "s/$var/replace/g" "$file"

Ich bekomme jedoch Fehler. Wie kann ich das Problem umgehen?

buydadip
quelle

Antworten:

193

Verwenden Sie ein alternatives Regex-Trennzeichen, sedmit dem Sie ein beliebiges Trennzeichen ( einschließlich Steuerzeichen ) verwenden können:

sed "s~$var~replace~g" $file
Anubhava
quelle
2
Funktioniert nicht Ich versuche sed -i "s ~ blah ~ $ var ~ g file" und erhalte: "sed -e Ausdruck # 1, char 70: nicht abgeschlossener` s'-Befehl ". Ich habe bestätigt, dass der Schuldige ein Schrägstrich in $ var ist. Varianten dieser Lösung sind überall und keine von ihnen funktioniert. Wurde sed kürzlich aktualisiert? Ich bin auf v4.2.2 unter Ubuntu 14.04.4 LTS.
Paul Ericson
Das hängt davon ab, was Wert ist$var
anubhava
1
Funktioniert nicht mit ~, wie @PaulEricson beobachtet hat, funktioniert aber trotzdem mit |
Kenny Ho
1
Das letzte "~" (nach g) scheint nicht benötigt zu werden, zumindest für AWS Amazon Linux (CentOS-basiert)
Saúl Martínez Vidals
1
Du hast meinen Tag gerettet
user7131571
60

Eine reine Bash-Antwort: Verwenden Sie die Parametererweiterung , um Schrägstriche in der Variablen rückgängig zu machen:

var="/Users/Documents/name/file"
sed "s/${var//\//\\/}/replace/g" $file
Glenn Jackman
quelle
1
Dies ist ein guter Weg, da Sie nicht immer wissen, welche Zeichen die Variable enthalten. So können Sie im Zweifelsfall immer dem sed-Trennzeichen entkommen. Zum Beispiel: sed "s: $ {var //: / \\:}: replace: g" $ file
Stephane L
Wunderschönen. Einige meiner Umbenennungsskripte wurden viel sauberer.
Wolke
Habe heute etwas Schönes gelernt, danke. Sollte die akzeptierte Antwort sein.
Steve Kehlet
6
Einige Klarheit über das hier verwendete Muster : ${parameter/pattern/string}. In diesem Fall ist der Parameter also vardas Muster /\/und die Zeichenfolge \\/. Alle Instanzen des Musters werden ersetzt, da das Muster mit a beginnt /.
Josh
1
@josh, und so ist der führende Schrägstrich des Musters nicht Teil des zu ersetzenden Musters.
Glenn Jackman
32

Eine andere Möglichkeit, dies zu tun, ist hässlicher als die Antwort von Anubhava, indem Sie varmit einem anderen sedBefehl allen Backslashes entkommen :

var=$(echo "$var" | sed 's/\//\\\//g')

dann wird dies funktionieren:

sed "s/$var/replace/g" $file
buydadip
quelle
12

Die Verwendung von /in sed als Trennzeichen führt zu Konflikten mit den Schrägstrichen in der Variablen, wenn diese ersetzt werden, und als Ergebnis wird eine Fehlermeldung angezeigt. Eine Möglichkeit, dies zu umgehen, besteht darin, ein anderes Trennzeichen zu verwenden, das für alle Zeichen in dieser Variablen eindeutig ist.

var="/Users/Documents/name/file"

Sie können das Octothorpe-Zeichen verwenden, das zum Anlass passt (oder jedes andere Zeichen, das nicht einfach zu /bedienen ist).

sed "s#$var#replace#g" 

oder

sed 's#$'$var'#replace#g'

Dies ist geeignet, wenn die Variable keine Leerzeichen enthält

oder

sed 's#$"'$var'"#replace#g'

Es ist klüger, das oben Gesagte zu verwenden, da wir daran interessiert sind, alles in dieser Variablen zu ersetzen, anstatt den gesamten Befehl doppelt in Anführungszeichen zu setzen, was dazu führen kann, dass Ihre Shell jedes Zeichen interpetiert, das als spezielles Shell-Zeichen für die Shell angesehen werden könnte.

repzero
quelle
Funktioniert nicht Ich versuche sed -i "s ~ blah ~ $ var ~ g file" und erhalte: "sed -e Ausdruck # 1, char 70: nicht abgeschlossener` s'-Befehl ". Ich habe bestätigt, dass der Schuldige ein Schrägstrich in $ var ist. Varianten dieser Lösung sind überall und keine von ihnen funktioniert. Wurde sed kürzlich aktualisiert? Ich bin auf v4.2.2 unter Ubuntu 14.04.4 LTS.
Paul Ericson
@PaulEricson Ich kann dies auf keinem mir zur Verfügung stehenden Ubuntu wiederholen. Wenn Ihr $varenthält ~, müssen Sie natürlich ein anderes Trennzeichen auswählen. Der Fehler "nicht abgeschlossen" klingt so, als ob Ihr Fehler $vareine neue Zeile enthält. Sie können das Problem möglicherweise beheben, indem Sie jede neue Zeile im Wert mit einem Backslash versehen, aber ich denke nicht, dass dies vollständig portabel ist. Dies hängt im Großen und Ganzen nicht mit dem Problem der Schrägstriche im Wert zusammen.
Tripleee
@PaulEricson Oh, und Ihr Zitat ist falsch. Sie sollten den Dateinamen nicht in Anführungszeichen setzen. sed -i "s~blah~$var~g" filemit Anführungszeichen nur um das eigentliche sedSkript.
Tripleee
5

Dies ist eine alte Frage, aber keine der Antworten hier behandelt andere Operationen als s/from/to/ausführlich.

Die allgemeine Form einer sedAussage ist

*address* *action*

Dabei kann die Adresse ein Regex-Bereich oder ein Zeilennummernbereich sein (oder leer. In diesem Fall wird die Aktion auf jede Eingabezeile angewendet). So zum Beispiel

sed '1,4d' file

löscht die Zeilen 1 bis 4 (die Adresse ist der Zeilennummernbereich 1,4und die Aktion ist der dLöschbefehl); und

sed '/ick/,$s/foo/bar/' file

werde das erste Vorkommen ersetzen foomit barauf jeder Leitung zwischen dem ersten Spiel auf dem regulären Ausdruck ickund das Ende der Datei (die Adresse ist die Reichweite /ick/,$und die Aktion ist das sErsatzbefehl s/foo/bar/).

In diesem Zusammenhang ickkönnten Sie dies tun , wenn Sie von einer Variablen stammen

sed "/$variable/,\$s/foo/bar/"

(Beachten Sie die Verwendung von doppelten Anführungszeichen anstelle von einfachen Anführungszeichen, damit die Shell die Variable interpolieren kann, und die Notwendigkeit, das wörtliche Dollarzeichen in doppelten Anführungszeichen zu setzen.) Wenn die Variable jedoch einen Schrägstrich enthält, wird ein Syntaxfehler angezeigt. (Die Shell erweitert die Variable und übergibt die resultierende Zeichenfolge an sed; sedsieht also nur Literaltext - sie hat kein Konzept für die Variablen der Shell.)

Die Lösung besteht darin, ein anderes Trennzeichen zu verwenden (wobei Sie natürlich in der Lage sein müssen, ein Zeichen zu verwenden, das im Wert der Variablen nicht vorkommen kann). Im Gegensatz zu diesem s%foo%bar%Fall benötigen Sie jedoch auch einen Backslash vor dem Trennzeichen, wenn Sie ein anderes Trennzeichen verwenden möchten Trennzeichen als Standard /:

sed "\\%$variable%,\$s/foo/bar/" file

(In einfachen Anführungszeichen würde ein einziger Backslash offensichtlich ausreichen); oder Sie können jeden Schrägstrich im Wert separat maskieren. Diese spezielle Syntax ist nur Bash:

sed "/${variable//\//\\/}/,\$s/foo/bar/" file

oder wenn Sie eine andere Shell verwenden, versuchen Sie es

escaped=$(echo "$variable" | sed 's%/%\\/%g')
sed "s/$escaped/,\$s/foo/bar/" file

Aus Gründen der Klarheit wären die obigen Befehle äquivalent zu , wenn $variablesie die Zeichenfolge enthalten 1/2würden

sed '\%1/2%,$s/foo/bar/' file

im ersten Fall und

sed '/1\/2/,$s/foo/bar/' file

in dieser Sekunde.

Tripleee
quelle
4

Verwenden Sie Perl, bei dem Variablen erstklassige Bürger sind und nicht nur Makros erweitern:

var=/Users/Documents/name/file perl -pe 's/\Q$ENV{var}/replace/g' $file
  • -p liest die Eingabe zeilenweise und druckt die Zeile nach der Verarbeitung
  • \Qzitiert alle Metazeichen in der folgenden Zeichenfolge (wird für den hier dargestellten Wert nicht benötigt, ist jedoch erforderlich, wenn der Wert enthalten ist [oder einige andere Werte, die für reguläre Ausdrücke speziell sind).
Choroba
quelle
Die einzige allgemein nützliche Antwort auf Dutzende von Fragen zum Verwenden von Variablen in sed expression in Stack Exchange. Alles andere ist effektiv "alles zu entkommen, was für sed besonders ist", was angesichts der Variabilität von Variablen und sed praktisch nutzlos ist.
Heath Raftery