sed: Wie wird die Zeile ersetzt, wenn sie gefunden wurde, oder an das Dateiende angehängt, wenn sie nicht gefunden wurde?


34

Ist es bei einer einzelnen Eingabedatei, die nur Kommentare (beginnend mit #) und VARIABLE = Wertzeilen enthält, möglich, einen Wert für eine einzelne Variable zu ersetzen, wenn sie gefunden wird, und das Paar andernfalls an das Dateiende anzuhängen, wenn sie nicht gefunden wird?

Meine aktuelle Methode löscht sie in einem ersten Durchgang und hängt sie dann in einem zweiten Durchgang an das Ende der Datei an. Diese Methode bringt jedoch die Zeilenreihenfolge durcheinander (und besteht auch aus zwei verschiedenen Befehlen):

sed -r "/^FOOBAR=.*$/d"      -i samefile &&
sed -r "$ a\FOOBAR=newvalue" -i samefile

Gibt es sowieso dafür, dh. Zeilenreihenfolge in einer einzigen Sed-Zeile einhalten? Wenn ein anderes Dienstprogramm (awk, ...) dies tut, übernehme ich sed.

Antworten:


29

Es ist eigentlich ganz einfach mit sed: Wenn eine Zeile passt, kopieren Sie sie einfach in das halte Feld und sersetzen Sie den Wert.
In der letzten $Zeile xändern Sie den Hold Space und den Pattern Space und prüfen, ob dieser leer ist. Wenn es nicht leer ist, bedeutet dies, dass die Ersetzung bereits durchgeführt wurde, sodass nichts zu tun ist. Wenn es leer ist, bedeutet dies, dass keine Übereinstimmung gefunden wurde. Ersetzen Sie daher den Musterbereich durch die gewünschte Variable = Wert und hängen Sie ihn an die aktuelle Zeile im Haltepuffer an. Zum Schluss xnochmal ändern:

sed '/^FOOBAR=/{h;s/=.*/=newvalue/};${x;/^$/{s//FOOBAR=newvalue/;H};x}' infile

Das Obige ist gnu sedSyntax. Tragbar:

sed '/^FOOBAR=/{
h
s/=.*/=newvalue/
}
${
x
/^$/{
s//FOOBAR=newvalue/
H
}
x
}' infile

1
Wie würde das aussehen, wenn newvaluees in einer Variablen gespeichert ist?
user1810087

sed "/^${varName}=/{h;s/=.*/=${varValue}/};\${x;/^$/{s//${varName}=${varValue}/;H};x}" ${VARFILE}mit Variablen, doppelten Anführungszeichen, um das Ersetzen von Variablen zu ermöglichen, und einem $$$varNamesed "/^${varName}=/{h;s/=.*/=${!varName}/};\${x;/^$/{s//${varName}=${!varName}/;H};x}" ${VARFILE}
Escapezeichen, das

1
Diese Lösung ist nicht "einfach" lol. Aber es funktioniert. sed man-seiten sind nicht die einfachsten sichten, es wäre sehr hilfreich, wenn sie die
auswirkungen

18

Dies kann wahrscheinlich verkürzt werden. Es ist kein einziger sed-Befehl und es verwendet auch grep, aber dies scheint im Grunde das zu sein, was Sie wollen. Es ist eine einzelne Zeile und bearbeitet die Datei direkt (keine temporären Dateien).

grep -q "^FOOBAR=" file && sed "s/^FOOBAR=.*/FOOBAR=newvalue/" -i file || 
    sed "$ a\FOOBAR=newvalue" -i file

13

Hier ist ein einfacherer sedAnsatz, da ich es nicht sed hold spaceeinfach finde, damit zu arbeiten. Wenn Sie hold spacemit dem don_crissti-Ansatz vertraut sind , haben Sie zusätzlich die Möglichkeit, die vorhandene Linie zu erhalten. Dies ist jedoch normalerweise sehr selten.

Bei diesem Ansatz drucken Sie nur alle Zeilen außer der Zeile, die Sie löschen möchten, und hängen dann am Ende die Ersetzung an.

sed -n -e '/^FOOBAR=/!p' -e '$aFOOBAR=newvalue' infile

1
Diese Version ist sehr gut, wenn Sie eine Datei aktualisieren möchten, die möglicherweise einen vorhandenen, aber veralteten Wert hat.
Guillem

3

Basierend auf den anderen Antworten ersetzen Sie den Wert einer Variablen, wenn diese Variable in der Datei vorhanden ist, und hängen ihn an das Ende der Datei an, wenn dies nicht der Fall ist (was nicht der Fall ist, den Ihre veröffentlichten sedBefehle tun) könnte dies versuchen:

perl -ne '$c=1 if s/^FOOBAR=.*$/FOOBAR=newvalue/;  
             print; 
             END{print "FOBAR=newvalue" unless $c==1}' file > tmpfile && 
mv tmpfile file

1

In awk ist es ein bisschen einfacher, obwohl das "In-Place-Editieren" nicht automatisch erfolgt:

awk -v varname="FOOBAR" -v newval="newvalue" '
    BEGIN {FS = OFS = "="}
    $1 == varname {$2 = newval; found = 1}
    {print}
    END {if (! found) {print varname, newval}}
' file > tempfile &&
mv tempfile file

1

Verwenden Sie einfach grepund echo, um einen leeren Datensatz zu erstellen:

grep -q '^FOOBAR=' somefile || echo 'FOOBAR=VALUE' >> somefile
sed -i 's/FOOBAR=.*$/FOOBAR=VALUE/' somefile 

Jede Zeile wird mit dem Fehlercode Null ausgeblendet.


Sie verlieren die grepecho
Zeilenreihenfolge

In der Tat, wenn Sie die neuen Elemente am Ende der Datei einfügen, aber weiterhin bestellen, wenn Sie einen alten Wert ersetzen.
MUY Belgium

0

Funktioniert eigentlich mit: sed -i '/^FOOBAR=/{h;s/=.*/=newvalue/};${x;/^$/{s//FOOBAR=newvalue/;H};x}' infile In der gewählten Antwort fehlt -i.


3
Dies ist wirklich ein Kommentar und keine Antwort auf die ursprüngliche Frage. Wenn Sie einen Autor kritisieren oder um Klärung bitten möchten, hinterlassen Sie einen Kommentar unter seinem Beitrag. Sie können jederzeit Ihre eigenen Beiträge kommentieren. Wenn Sie über eine ausreichende Reputation verfügen, können Sie jeden Beitrag kommentieren . Bitte lesen Sie Warum brauche ich 50 Ruf, um einen Kommentar abzugeben? Was kann ich stattdessen tun?
DavidPostill

-1

Dies mit Perl und In-Place zu tun, funktioniert gut für mich:

grep ^FOOBAR= my.file && perl -i -ple "s/^FOOBAR=.+/FOOBAR=newvalue/g" my.file || echo FOOBAR=newvalue >> my.file

2
Diese Antwort ist in vielerlei Hinsicht schlecht ! grepund echound perlanstatt alles zu tun perl? Schreiben Sie auch in eine Datei ( >> my.file), während Sie daraus lesen ( grep my.file)?
Vladr
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.