Grep, um die richtige Zeile zu finden, sed, um den Inhalt zu ändern und ihn dann wieder in die Originaldatei zu setzen?

9

Ich versuche, ein einzelnes Wort in einer bestimmten Zeile in einer Datei zu ändern, habe jedoch Probleme, alle miteinander zu verbinden.

Grundsätzlich befindet sich in einer Zeile meiner Datei das Schlüsselwort 'firmware_revision', und in dieser Zeile (und nur in dieser Zeile) möchte ich das Wort 'test' durch das Wort 'Produktion' ersetzen.

Also kann ich das machen:

grep 'firmware_revision' myfile.py | sed 's/test/production'

Dadurch wird die gewünschte Zeile ausgewählt und die Ersetzung durchgeführt, aber ich kann nicht herausfinden, wie diese neue Zeile in die Originaldatei übernommen wird, um die alte Zeile zu ersetzen. Ich kann es natürlich nicht einfach zurück in die Datei umleiten. Was soll ich also tun?

Selbst wenn ich Provisorien verwende, grepverliere ich alle anderen Daten in der Datei, indem ich nur die benötigte Zeile erhalte, sodass ich nicht mehr nur eine temporäre Datei in eine temporäre Datei umleiten und dann das Original durch die temporäre Datei ersetzen kann.

Bearbeiten - Jemand hat nach weiteren Informationen gefragt

Nehmen wir an, ich habe eine Datei voller solcher Zeilen

[
  ('key_name1', str, 'value1', 'Description'),
  ('key_name2', str, 'value2', 'Description'),
  ('key_name3', str, 'value3', 'Description'),
  ('firmware_revision', str, 'my-firmware-name-test', 'Firmware revision name')
]

Jetzt möchte ich ein Skript (idealerweise ein Einzeiler) schreiben, das die Zeile mit 'firmware_revision' findet und alle Instanzen des Wortes 'test' in dieser Zeile in 'Produktion' ändert. Das Wort "Test" könnte sich an anderen Stellen in dieser Datei befinden, und ich möchte nicht, dass diese geändert werden. Um klar zu sein, möchte ich die obige Zeile in ändern

('firmware_revision', str, 'my-firmware-name-production', 'Firmware revision name')

Wie mache ich das?

John Allard
quelle
5
sedist sehr leistungsfähig, kann beide Funktionen ( grepund Ersetzungen) ausführen, aber wir benötigen weitere Informationen darüber, wie die Zeile aussieht, um Ihnen zu helfen.
gro♀
Ich werde dem ursprünglichen Beitrag weitere Informationen hinzufügen
John Allard
7
Versuchen Sie:sed -i.bak '/firmware_revision/ s/test/production/' myfile.py
John1024
1
Ah es hat perfekt funktioniert! Können Sie die Syntax erklären?
John Allard
@ JohnAllard Sehr gut. Ich habe eine Antwort mit Erklärung hinzugefügt.
John1024

Antworten:

22

Versuchen:

sed -i.bak '/firmware_revision/ s/test/production/' myfile.py

Hier /firmware_revision/wirkt als Bedingung. Dies gilt für Zeilen, die mit dem regulären Ausdruck übereinstimmen, firmware_revisionund für andere Zeilen. Wenn die Bedingung erfüllt ist, wird der folgende Befehl ausgeführt. In diesem Fall ist dieser Befehl ein Ersatzbefehl, der das erste Auftreten von testdurch ersetzt production.

Mit anderen Worten, der Befehl s/test/production/wird nur in Zeilen ausgeführt, die mit dem regulären Ausdruck übereinstimmen firmware_revision. Alle anderen Linien verlaufen unverändert.

Standardmäßig sendet sed seine Ausgabe an den Standardausgang. Sie wollten jedoch die Datei an Ort und Stelle ändern. Also haben wir die -iOption hinzugefügt . Dies -i.bakbewirkt insbesondere, dass die Datei mit einer Sicherungskopie geändert wird, die mit einer .bakErweiterung gespeichert wird .

Wenn Sie entschieden haben, dass der Befehl für Sie funktioniert und Sie gefährlich leben und kein Backup erstellen möchten, verwenden Sie mit GNU sed (Linux):

sed -i '/firmware_revision/ s/test/production/' myfile.py

Im Gegensatz dazu -imuss die Option unter BSD (OSX) ein Argument haben. Wenn Sie kein Backup erstellen möchten, geben Sie ein leeres Argument an. Verwenden Sie daher:

sed -i '' '/firmware_revision/ s/test/production/' myfile.py

Bearbeiten

Bei der Bearbeitung der Frage fordert das OP an, jedes Vorkommen testin der Zeile durch zu ersetzen production. In diesem Fall fügen wir gdem Ersatzbefehl die Option für eine globale (für diese Zeile) Ersetzung hinzu:

sed -i.bak '/firmware_revision/ s/test/production/g' myfile.py
John1024
quelle
5
Der Vollständigkeit halber möchten Sie möglicherweise auch den -i.bakTeil erläutern .
Stephen Harris
5
@ StephenHarris Guter Punkt. Erklärung von -ihinzugefügt.
John1024
Ich glaube, Sie könnten wahrscheinlich die Sterne erreichen, indem Sie einfach auf dem Buch stehen und erklären, sedwas alles möglich ist ...
KlaymenDK
Für die nicht BSD Menschen @ John1024, könnten Sie die Gründe des ersten hinzufügen ''nach -i?
Hastur
3
OP wollte alle Instanzen des Wortes "Test" in dieser Zeile in "Produktion" ändern . In diesem Fall ist es wichtig, / g an den Befehl sed anzuhängen: sed -i '/firmware_revision/ s/test/production/g' myfile.pyAndernfalls wird nur die erste Instanz geändert.
Knub
4

Auf älteren Maschinen mit Old-School sed, die keine Option unterstützen -i:

TF=$( mktemp -t "${0##*/}"_$$_XXXXXXXX ) && \
trap 'rm -f "$TF"' EXIT HUP INT QUIT TERM && \
sed '/firmware_revision/ s/test/production/' myfile.py >"$TF" && \
mv -f "$TF" myfile.py
Satō Katsura
quelle
1
Auf diesen älteren Maschinen können Sie es einfach verwenden edund in einer Zeile printf %s\\n g/firmware_revision/s/test/production/g w q | ed -s myfile.py
ausführen
1
@don_crissti Sehr wahr, bietet aber ed(1)keinen Vorwand, um die Verwendung von zu zeigen mktemp(1).
Satō Katsura
Wenn Sie eine temporäre Datei verwenden, können Sie den Inode und die Dauerwellen der Originaldatei genauso wie zuvor beibehalten ed. mv -f "$TF" myfile.plVerwenden Sie stattdessen cat "$TF" > myfile.pl && rm -f "$TF". Übrigens ist es empfehlenswert, Variablennamen in Kleinbuchstaben zu verwenden ( $tfanstelle von $TF). Sie stehen garantiert nicht in Konflikt mit integrierten Bash-Variablen (wahrscheinlich auch mit anderen Bourne-Shells).
Cas
@cas Re :: cat "$TF" > myfile.plversuche dies: touch a b; chmod 0444 a; cat b >a(Übrigens, sed -ifunktioniert auch in diesem Fall nicht). Lassen Sie den Benutzer besser damit umgehen. Re :: rm -f "$TF"nicht benötigt, siehe trap ... EXIT. Re: $tfstatt $TF: vielleicht; Es ist eine Frage des Stils.
Satō Katsura
Dies ist das normale und erwartete Verhalten von Unix-Dateiberechtigungen. Mein Punkt war, dass das Erstellen einer neuen Datei und das Verschieben über das Original 1. zu einem neuen Inode für diesen Dateinamen führt (wodurch alle Hardlinks beschädigt werden). 2. Ändern Sie möglicherweise die Dauerwelle, wenn der Strom umask von der ursprünglichen Dauerwelle abweicht. Bei Großbuchstaben ist es nicht nur eine Frage des Stils - viele Leute, die den Fehler gemacht haben, Großbuchstaben PATH oder RANDOM oder SHELL oder was auch immer für ihre eigenen Variablen zu verwenden, haben es auf die harte Tour herausgefunden. (und ja, ich benutze oft selbst Großbuchstaben. Ich weiß, dass es falsch ist, ich versuche, die Gewohnheit zu brechen).
Cas