Ersetzen Sie einen Teilstring durch einen anderen String im Shell-Skript


823

Ich habe "Ich liebe Suzi und heirate" und ich möchte "Suzi" in "Sara" ändern.

#!/bin/bash
firstString="I love Suzi and Marry"
secondString="Sara"
# do something...

Das Ergebnis muss folgendermaßen aussehen:

firstString="I love Sara and Marry"

17
Ich würde damit beginnen, Suzi sanft im Stich zu lassen. :)
Codesniffer

Es liegt nicht an dir, sondern an mir ! das hätte die Saite sein sollen, um mit Suzi zu sprechen
GoodSp33d

Antworten:


1360

Verwenden Sie Folgendes, um das erste Auftreten eines Musters durch eine bestimmte Zeichenfolge zu ersetzen :${parameter/pattern/string}

#!/bin/bash
firstString="I love Suzi and Marry"
secondString="Sara"
echo "${firstString/Suzi/$secondString}"    
# prints 'I love Sara and Marry'

Verwenden Sie zum Ersetzen aller Vorkommen Folgendes :${parameter//pattern/string}

message='The secret code is 12345'
echo "${message//[0-9]/X}"           
# prints 'The secret code is XXXXX'

(Dies ist in dokumentiert das Bash - Referenzhandbuch , §3.5.3 "Shell Parameter Expansion" .)

Beachten Sie, dass diese Funktion nicht von POSIX spezifiziert wird - es handelt sich um eine Bash-Erweiterung -, sodass nicht alle Unix-Shells sie implementieren. Die relevante POSIX-Dokumentation finden Sie in den Open Group Technical Standard- Basisspezifikationen , Ausgabe 7 , Shell & Utilities- Volume, §2.6.2 "Parametererweiterung" .


3
@ruakh wie schreibe ich diese Aussage mit einer oder Bedingung. Nur wenn ich Suzi oder Marry durch eine neue Saite ersetzen möchte.
Priyatham51

3
@ Priyatham51: Dafür gibt es keine eingebaute Funktion. Ersetzen Sie einfach einen und dann den anderen.
Ruakh

4
@Bu: Nein, denn \nin diesem Zusammenhang würde sich selbst keine Newline darstellen. Ich habe Bash momentan nicht zum Testen zur Hand, aber Sie sollten in der Lage sein, so etwas wie zu schreiben $STRING="${STRING/$'\n'/<br />}". (Obwohl Sie wahrscheinlich wollen STRING//- ersetzen Sie alle - anstatt nur STRING/.)
Ruakh

25
Um klar zu sein, da mich das ein bisschen verwirrt hat, muss der erste Teil eine variable Referenz sein. Das kannst du nicht machen echo ${my string foo/foo/bar}. Sie würden braucheninput="my string foo"; echo ${input/foo/bar}
Henrik N

2
@mgutt: Diese Antwort verweist auf die entsprechende Dokumentation. Die Dokumentation ist nicht perfekt - anstatt //direkt zu erwähnen , wird beispielsweise erwähnt, was passiert, wenn das Muster mit ' /' beginnt “(was nicht einmal genau ist, da der Rest dieses Satzes davon ausgeht, dass das Extra /nicht Teil von ist das Muster) - aber ich kenne keine bessere Quelle.
Ruakh

208

Dies kann vollständig mit Bash-String-Manipulation erfolgen:

first="I love Suzy and Mary"
second="Sara"
first=${first/Suzy/$second}

Das wird nur das erste Vorkommen ersetzen; Um sie alle zu ersetzen, verdoppeln Sie den ersten Schrägstrich:

first="Suzy, Suzy, Suzy"
second="Sara"
first=${first//Suzy/$second}
# first is now "Sara, Sara, Sara"

9
Was bringt es, eine Antwort zu haben, die die akzeptierte dupliziert, anstatt die akzeptierte mit der globalen Ersatzoption zu bearbeiten? Und das Hinzufügen dieser regulären Ausdrücke wird dabei unterstützt. Und erklären, was mit "Bad Substitution" los ist. Sie haben genug Punkte, @ Kevin :)
Dan Dascalescu

6
Es scheint, dass beide genau in der gleichen Minute geantwortet haben: O
Jamie Hutber

76

Für Dash funktionieren alle vorherigen Beiträge nicht

Die POSIX- shkompatible Lösung lautet:

result=$(echo "$firstString" | sed "s/Suzi/$secondString/")

1
Ich habe folgendes: $ echo $ (echo $ firstString | sed 's / Suzi / $ secondString / g') Ich liebe $ secondString und Marry
Qstnr_La

2
@ Qstnr_La verwenden doppelte Anführungszeichen für die Substitution von Variablen:result=$(echo $firstString | sed "s/Suzi/$secondString/g")
emc

1
Plus 1, um zu zeigen, wie auch eine Variable ausgegeben wird. Vielen Dank!
Chef Pharao

2
Ich habe die einfachen Anführungszeichen korrigiert und auch die fehlenden Anführungszeichen um das echoArgument hinzugefügt . Es funktioniert täuschend ohne Anführungszeichen mit einfachen Zeichenfolgen, bricht jedoch leicht bei nicht trivialen Eingabezeichenfolgen (unregelmäßiger Abstand, Shell-Metazeichen usw.).
Tripleee

In sh (AWS Codebuild / Ubuntu sh) habe ich festgestellt, dass ich am Ende einen einzelnen Schrägstrich brauche, keinen doppelten. Ich werde den Kommentar bearbeiten, da die obigen Kommentare auch einen einzelnen Schrägstrich zeigen.
Tim

67

Versuche dies:

 sed "s/Suzi/$secondString/g" <<<"$firstString"

19
Sie brauchen Sed dafür nicht wirklich; Bash unterstützt diese Art von Ersatz nativ.
Ruakh

12
Ich denke, dies ist mit "bash" markiert, kam aber hierher, weil es etwas Einfaches für eine andere Shell brauchte. Dies ist eine schöne prägnante Alternative zu dem, was wiki.ubuntu.com/… so aussehen ließ, als würde ich es brauchen.
Natevw

3
Dies funktioniert hervorragend für Ash / Dash oder andere POSIX sh.
Yoshua Wuyts

2
Ich erhalte den Fehler sed: -e Ausdruck # 1, Zeichen 9: Unbekannte Option für `s
Nam G VU

1
Erste Antwort, die mit stdin funktioniert, ideal für Pipelining-Strings.
Dinei

46

Es ist besser, bash zu verwenden, als sedwenn Strings RegExp-Zeichen haben.

echo ${first_string/Suzi/$second_string}

Es ist portabel für Windows und funktioniert mit mindestens so alt wie Bash 3.1.

Um zu zeigen, dass Sie sich keine großen Sorgen um die Flucht machen müssen, drehen wir Folgendes:

/home/name/foo/bar

Das sehr gut finden:

~/foo/bar

Aber nur wenn /home/nameam Anfang ist. Wir brauchen nicht sed!

Da bash gibt uns magische Variablen $PWDund $HOMEkönnen wir:

echo "${PWD/#$HOME/\~}"

EDIT: Danke für Mark Haferkamp in den Kommentaren für den Hinweis zum Zitieren / Entkommen ~. *

Beachten Sie, wie die Variable $HOMESchrägstriche enthält, aber dies hat nichts kaputt gemacht.

Weiterführende Literatur: Advanced Bash-Scripting Guide .
Wenn die Verwendung sedein Muss ist, müssen Sie jedem Zeichen entkommen .


Diese Antwort hat mich daran gehindert, sedden pwdBefehl zu verwenden, um zu vermeiden, dass bei jeder Ausführung meiner benutzerdefinierten Datei eine neue Variable definiert wird $PS1. Bietet Bash eine allgemeinere Möglichkeit als magische Variablen, die Ausgabe eines Befehls zum Ersetzen von Zeichenfolgen zu verwenden? Was Ihren Code angeht, musste ich mich dem entziehen ~, um Bash davon abzuhalten, ihn in $ HOME zu erweitern. Was macht das #in Ihrem Befehl?
Mark Haferkamp

1
@MarkHaferkamp Siehe den Link "Weiterführende Literatur empfohlen". Über "Flucht vor dem ~": Beachten Sie, wie ich Sachen zitiert habe. Denken Sie daran, immer Sachen zu zitieren! Und dies funktioniert nicht nur für magische Variablen: Jede Variable kann innerhalb von Bash ersetzt, die Länge der Zeichenfolge ermittelt und vieles mehr werden. Herzlichen Glückwunsch zum $PS1schnellen Versuch : Sie könnten auch daran interessiert sein, $PROMPT_COMMANDob Sie sich in einer anderen Programmiersprache besser auskennen und eine kompilierte Eingabeaufforderung codieren möchten.
Camilo Martin

Der Link "Weiterlesen" erklärt das "#". Auf Bash 4.3.30, echo "${PWD/#$HOME/~}"ersetzt nicht meine $HOMEmit ~. Ersetzen ~durch \~oder '~'funktioniert. Alle diese Arbeiten an Bash 4.2.53 in einer anderen Distribution. Können Sie bitte Ihren Beitrag aktualisieren, um ihn ~für eine bessere Kompatibilität zu zitieren oder zu umgehen? Was ich mit meiner Frage "magische Variablen" meinte, war: Kann ich Bashs Variablensubstitution beispielsweise für die Ausgabe von verwenden, unameohne sie zuerst als Variable zu speichern? Was meine persönlichen $PROMPT_COMMAND, ist es kompliziert .
Mark Haferkamp

@ MarkHaferkamp Whoa, du hast vollkommen recht, mein schlechtes. Aktualisiert die Antwort jetzt.
Camilo Martin

1
@ MarkHaferkamp Bash und seine obskuren Fallstricke ...: P
Camilo Martin

19

Wenn Sie morgen entscheiden, dass Sie Marry nicht lieben, kann sie auch ersetzt werden:

today=$(</tmp/lovers.txt)
tomorrow="${today//Suzi/Sara}"
echo "${tomorrow//Marry/Jesica}" > /tmp/lovers.txt

Es muss 50 Möglichkeiten geben, Ihren Geliebten zu verlassen.


9

Da kann ich keinen Kommentar hinzufügen. @ruaka Um das Beispiel besser lesbar zu machen, schreiben Sie es so

full_string="I love Suzy and Mary"
search_string="Suzy"
replace_string="Sara"
my_string=${full_string/$search_string/$replace_string}
or
my_string=${full_string/Suzy/Sarah}

Bis ich zu Ihrem Beispiel kam, hatte ich die Reihenfolge umgekehrt verstanden. Dies half zu klären, was passiert
raghav710

5

Reine POSIX - Shell - Methode, die im Gegensatz zu Roman Kazanovskyi ‚s sed-basierte Antwort keine externen Tools benötigt, nur um die eigene Mutter Shell Parameter Erweiterungen . Beachten Sie, dass lange Dateinamen minimiert werden, damit der Code besser in eine Zeile passt:

f="I love Suzi and Marry"
s=Sara
t=Suzi
[ "${f%$t*}" != "$f" ] && f="${f%$t*}$s${f#*$t}"
echo "$f"

Ausgabe:

I love Sara and Marry

Wie es funktioniert:

  • Kleinstes Suffixmuster entfernen. "${f%$t*}"gibt " I love" zurück, wenn das Suffix $t" Suzi*" in $f" " steht. I love Suzi and Marry

  • Aber wenn t=Zelda, dann "${f%$t*}"löscht nichts und gibt die gesamte Zeichenfolge " I love Suzi and Marry" zurück.

  • Dies wird zu Test verwendet , wenn $tin $fmit [ "${f%$t*}" != "$f" ]dem bewerten wird wahr , wenn die $fZeichenfolge „ Suzi*“ und falsch , wenn nicht.

  • Wenn der Test true zurückgibt , erstellen Sie die gewünschte Zeichenfolge mit " Kleinstes Suffixmuster entfernen ${f%$t*} " I love"und" Kleinstes Präfixmuster entfernen ${f#*$t} " and Marry", wobei die zweite Zeichenfolge $s" Sara" dazwischen liegt.


5

Ich weiß, dass dies alt ist, aber da niemand über die Verwendung von awk gesprochen hat:

    firstString="I love Suzi and Marry"
    echo $firstString | awk '{gsub("Suzi","Sara"); print}'

1
Dieser Trick ist der richtige Weg, wenn das, was Sie teilen möchten, nicht in einer Variablen, sondern in einer Textdatei enthalten ist. Danke, @Payam!
Felipe SS Schneider

2
echo [string] | sed "s/[original]/[target]/g"
  • "s" bedeutet "Ersatz"
  • "g" bedeutet "global, alle übereinstimmenden Vorkommen"
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.