Wie kann ich Variablen in LHS und RHS einer sed-Substitution verwenden?

61

Ich will das tun:

cat update_via_sed.sh | sed 's/old_name/new_name/' > new_update_via_sed.sh

in meinem Programm.

Ich möchte aber Variablen verwenden, z

old_run='old_name_952'
new_run='old_name_953'

Ich habe versucht, sie zu verwenden, aber die Ersetzung findet nicht statt (kein Fehler). Ich habe versucht:

cat update_via_sed.sh | sed 's/old_run/new_run/'
cat update_via_sed.sh | sed 's/$old_run/$new_run/'
cat update_via_sed.sh | sed 's/${old_run}/${new_run}/'
Michael Durrant
quelle
2
Die Antwort finden Sie unter Verwenden eines Parameters in einem Befehlsargument .
Manatwork

Antworten:

45

Du könntest es tun:

sed "s/$old_run/$new_run/" < infile > outfile

Aber Vorsicht , dass $old_runwürde als regulärer Ausdruck genommen werden und so alle Sonderzeichen , die die Variable enthält, wie zum Beispiel /oder .müßten entwertet werden . In ähnlicher Weise müssten $new_rundie Zeichen &und \speziell behandelt werden, und Sie müssten die /Zeichen und die Zeilenumbrüche in diesen Zeichen umgehen.

Wenn der Inhalt von $old_runoder $new_runnicht unter Ihrer Kontrolle steht, ist es wichtig, dass dieser Code ausgeblendet wird .

Stéphane Chazelas
quelle
3
Ich habe einfache Anführungszeichen in sed params verwendet, auf doppelte Anführungszeichen umgestellt und es hat funktioniert. Genial.
Aakash
es ist nicht so, dass sedallein nicht Regex verwenden wird, aber sed -Ewürde?
Kiwy
@ Kiwy, nein. -eist ein angeben sed e xpression , so dass Sie mehr als eine (wie in Pass sed -e exp1 -e exp2) oder Kombinationen von Dateien und Ausdrücke ( sed -f file -e exp). -EBei einigen Implementierungen kann es verwirrend sein , dass sed EREs anstelle von BREs verwendet.
Stéphane Chazelas
Ich habe mich vertippt, aber OK, das erklärt, warum ich so viele Probleme mit sed habe, ohne dass -Eich nur die erweiterte Regex beherrsche, nicht die reguläre. Danke für die Erklärung.
Kiwy
@Kiwy, in den verknüpften Fragen und Antworten finden Sie weitere Optionen, die von einigen sedImplementierungen für noch regulärere Ausdruckssyntaxen unterstützt werden.
Stéphane Chazelas
31

Das hat funktioniert:

cat update_via_sed.sh | sed 's/'"$old_run"'/'"$new_run"'/'

Da ich dieselbe Datei "wiederverwenden" möchte, verwende ich diese Option für alle, die einen ähnlichen Ansatz wünschen:

cat update_via_sed.sh | sed 's/'"$old_run"'/'"$new_run"'/' > new_update; mv -f new_update update_via_sed.sh

Die oben erstellte neue Datei löscht dann die aktuelle Datei und benennt die neue Datei in die aktuelle Datei um.

Michael Durrant
quelle
4
Sie brauchen keine Katze, kein MV oder zusätzliches "", es sei denn, Sie werden Sonderzeichen in ihnen haben. So ist es sed -i s/"$old"/"$new"/ update_via_sed.sh. Beachten Sie jedoch, dass dies bei keinem Wert von Alt und Neu funktioniert. zB darf es nicht / etc. enthalten, da $ old immer noch eine Suche ist und $ new ein Ersetzungsmuster, bei dem einige Zeichen spezielle Bedeutungen haben.
Frostschutz
1
sed -i "s/$old/$new/"ist auch in Ordnung (mit den oben genannten Vorsichtsmaßnahmen). sedBetrachtet das Trennzeichen normalerweise als das erste Zeichen nach dem sBefehl - dh wenn Ihre Variablen Schrägstriche enthalten, /aber nicht mit at ( @) angegeben werden, können Sie "s @ $ old @ $ new @" verwenden.
Peterph
22

Sie können Variablen in sed verwenden, solange sie in doppelten Anführungszeichen stehen (nicht in einfachen Anführungszeichen):

sed "s/$var/r_str/g" file_name >new_file

Wenn /die Variable einen Schrägstrich ( ) enthält, verwenden Sie ein anderes Trennzeichen wie unten

sed "s|$var|r_str|g" file_name >new_file
mani
quelle
1
+1 für die Erwähnung von Anführungszeichen. Das war mein problem
Speckledcarp
1
Genau das, was ich suchte, einschließlich der Verwendung von |wie in meinem Fall, enthalten die Variablen einen Pfad so hat es /darin
yorch
Aus irgendeinem Grund |funktionierte die Pipe für mich unter MacOS 10.14, aber andere Separatoren mögen @oder #nicht. Ich hatte auch Schrägstriche in meiner Umgebung.
Davidenke
21

"in-place" sed (mit der -i Flagge) war die Antwort. Danke an peterph.

sed -i "s@$old@$new@" file
Michael Durrant
quelle
2
Sie haben die Anführungszeichen an der falschen Stelle. Anführungszeichen müssen um Variablen stehen, nicht s@(was in keiner Weise speziell für die Shell ist). Am besten setzen Sie alles in Anführungszeichen wie sed -i "s@$old@$new@" file. Beachten Sie, dass dies -ikeine Standardoption ist sed.
Stéphane Chazelas
Wie kann ich $in diesem Befehl entkommen . Ich werde $xxxals Ganzes den Wert einer Variablen ändern $JAVA_HOME. Ich habe versucht sed -i "s@\$xxx@$JAVA_HOME@" file, aber Fehler sagtno previous regular expression
Hakunami
3

man bash gibt dies über einfache Anführungszeichen

Das Einschließen von Zeichen in einfache Anführungszeichen behält den Literalwert jedes Zeichens in den Anführungszeichen bei. Ein einfaches Anführungszeichen darf nicht zwischen einfachen Anführungszeichen stehen, auch wenn ein Backslash vorangestellt ist.

Was auch immer Sie in der Befehlszeile eingeben, die Bash interpretiert es und sendet dann das Ergebnis an das Programm, an das es gesendet werden soll. In diesem Fall erkennt die sed 's/$old_run/$new_run/'Bash zuerst das sedals ausführbare Datei in der $PATHVariablen . Die sedausführbare Datei erfordert eine Eingabe. Bash sucht nach der Eingabe und findet 's/$old_run/$new_run/'. Einfache Anführungszeichen sagen, dass Bash den Inhalt in ihnen nicht interpretiert und sie so weitergibt, wie sie sind. Bash übergibt sie dann an sed. Sed gibt einen Fehler aus, da dieser $nur am Zeilenende auftreten kann.

Wenn wir stattdessen doppelte Anführungszeichen verwenden, wird dies von "s/$old_run/$new_run/"bash $old_runals Variablenname interpretiert und ersetzt (diese Phase wird als Variablenerweiterung bezeichnet). Dies ist in der Tat das, was wir benötigten.

Sie müssen jedoch vorsichtig sein, wenn Sie doppelte Anführungszeichen verwenden, da diese zuerst von bash interpretiert und dann sed übergeben werden. Daher müssen einige Symbole wie `vor der Verwendung maskiert werden.

Nitishch
quelle
1

Bei Gefahr, frivol - haben Sie darüber nachgedacht perl.

Was - unter anderem - ziemlich gut darin ist, Operationen im Sed-Style auszuführen, aber auch ein vollwertigeres Programmier-Dummy.

Es sieht so aus, als ob Sie in Ihrem Beispiel tatsächlich versuchen, eine Zahl zu erhöhen.

Wie wäre es also mit:

#!/usr/bin/env perl
use strict;
use warnings 'all'; 

while ( <DATA> ) {
    s/old_name_(\d+)/"old_name_".($1+1)/e;
    print;
}

__DATA__
old_name_932

oder als Einzeiler:

perl -pe 's/old_name_(\d+)/"old_name_".($1+1)/e'
Sobrique
quelle
1
$ echo $d1 | sed -i 's/zkserver1/'${d1}'/g' deva_file
sed: -e expression #1, char 26: unterminated `s' command 

In $d1speichere ich Werte

Ich kann den Wert aus der Variablen nicht ersetzen, daher habe ich den folgenden Befehl ausprobiert, der funktioniert

echo $d1 | sed -i 's/zkserver1/'"${d1}"'/g' deva_file

fehlendes doppeltes Anführungszeichen in "${d1}", das war der Fehler

deva
quelle
-2

Vorausgesetzt $old_runund $new_runbereits gesetzt:

cat update_via_sed.sh | eval $(print "sed 's/$old_run/$new_run/g'")

Dies zeigt die Änderung auf dem Bildschirm. Sie können die Ausgabe bei Bedarf in eine Datei umleiten.

Orlando
quelle
-2

Um eine Variable in sed zu verwenden, verwenden Sie doppelte Anführungszeichen "", um die Variable in das von Ihnen verwendete Muster einzuschließen. Zum Beispiel: a = "Hi" Wenn Sie die Variable 'a' an ein Muster anhängen möchten, können Sie Folgendes verwenden: sed -n "1, / pattern" $ {a} "pattern / p" filename

Sher
quelle
2
Die Erweiterung von $aist hier nicht in Anführungszeichen gesetzt, was bedeutet, dass Leerzeichen in ihrem Wert die Wortteilung in der Shell auslösen.
Kusalananda
-2

Sie können auch Perl verwenden:

perl -pi -e ""s/$val1/$val2/g"" file
Samba
quelle
Betrachten Sie Variablen, deren Werte Leerzeichen enthalten können. Die leeren Zeichenfolgen um den Perl-Ausdruck ( "") bewirken nichts.
Kusalananda
-2

reiße die Saite

bad => linux sehe einen string $ old_run:

sed 's/$old_run/$new_run/'

gut => Linux sehe eine Variable $ old_run:

sed 's/'$old_run'/'$new_run'/'
marcdahan
quelle
1
Und wenn $old_runoder $new_runenthält Leerzeichen?
Kusalananda