
Ich habe eine Datei, die Text enthält und - in einer einzigen Zeile - einen Marker, der angibt, wo neuer Inhalt hinzugefügt werden soll
foo
bar
%SUBSTITUTE%
foo
Die Zeile line substitute sollte durch einen neuen mehrzeiligen String ersetzt werden text="some text"
(beachten Sie, dass ich den String nicht kenne, er könnte beispielsweise das Ergebnis des Lesens einer Datei sein text=
cat "file"``). Das Ergebnis des Ersetzens sollte lauten
foo
bar
some text
%SUBSTITUTE%
foo
Ich hatte eine funktionierende Version, die auf perl
dieser basierte, aber nicht mehr funktionierte (anscheinend aufgrund einer Änderung der Perl-Version). Jetzt versuche ich, die Zeile mit Standarddienstprogrammen wie tr
und sed
zu ersetzen. Ich habe damit einige Probleme, weil die einzufügende Zeichenfolge beliebige Zeichen enthalten kann, darunter auch Backslashs usw. Ich möchte diese vor dem Einfügen nicht maskieren.
Gibt es eine sichere Möglichkeit, die mit Standardtools funktioniert? Die anderen Fragen, die ich zu dem Problem finde, sind spezielle Lösungen, bei denen der einzufügende Text bekannt ist.
Antwort1
Wenn Sie die GNU-Version von sed haben, sollten Sie den Befehl verwenden können, r
um neuen Inhalt aus einer Datei zu lesen und einzufügen und dann die Markierungszeile zu löschen, z. B.
sed '/%SUBSTITUTE%/{
r path/to/newcontent
d
}' file
%SUBSTITUTE%
Wenn Sie den Marker beibehalten möchtennachdas Einfügen, das ist schwierig, denn was auch immer Sie tun, die GNU- r
Erweiterung stellt den Dateiinhalt in die Warteschlange, bis dieEndedes aktuellen Musterzyklus (beibehaltenVorwäre nur eine Frage des Entfernens des d
Befehls). Der wahrscheinlich einfachste Weg ist, ihn an die Datei anzuhängen newcontent
: Sie können das im Handumdrehen tun, wie
sed '/%SUBSTITUTE%/{
r /dev/stdin
d
}' file < <(sed '$a %SUBSTITUTE%' path/to/newcontent)
Mit einem ganz anderen Ansatz könnten Sie die erste Datei aufteilen %SUBSTITUTE%
und dann den neuen Inhalt hinzufügen.
csplit -s file '/%SUBSTITUTE%/'
cat xx00 newcontent xx01
Sie könnten auch eine Bash- read
Schleife über die Zeilen der ersten Datei ausführen und die neue Inhaltsdatei caten, wenn Sie die Markierungszeichenfolge finden. Ich habe jedoch in diesem Forum schon viel Kritik dafür bekommen, dass ich Leseschleifen für die Textverarbeitung vorgeschlagen habe. Leider bietet keines von beiden eine Lösung direkt vor Ort.
Antwort2
Hier ist eine weitere Möglichkeit mit ed
.
Fügen Sie den gesamten Inhalt von FILE
vor der Markierung ein (also vor der Zeile, die enthält %SUBSTITUTE%
):
ed -s originalfile <<< $'/%SUBSTITUTE%/- r FILE\nw\nq'
wobei:
/%SUBSTITUTE%/
: Adresse auf die erste passende Zeile setzt %SUBSTITUTE%
-
oder -1
: Adresse eine Zeile davor verschiebt
r FILE
: Reads FILE to after the addressed line.
w
: schreibt in originalfile
(ersetzen durch , ,p
um den Inhalt nur auszudrucken statt zu schreiben)
q
: Editor beenden
Das Ersetzen FILE
durch !echo "$TEXT"
fügt den Inhalt von $TEXT
vor der Markierung ein:
export TEXT
ed -s originalfile <<'IN'
/%SUBSTITUTE%/-1 r !echo "$TEXT"
w
q
IN
Antwort3
Ich habe die folgende Lösung gefunden, basierend auf awk
:
text=`cat "filepaste"`
export text;
<"$file" awk '
BEGIN {REPLACE=ENVIRON["text"] "\n%SUBSTITUTE%" }
{gsub(/^%SUBSTITUTE%$/, REPLACE); print}
'
Hier enthält "filepaste" den Inhalt, der ersetzt werden soll %SUBSTITUTE%
. Ein Vorteil ist, dass dieser String mit verschiedenen Shell-Tools bearbeitet werden kann, ohne dass er wieder in einer Datei gespeichert werden muss. Das Lesen der awk
Variable REPLACE
aus der Umgebungsvariable vermeidet die Erweiterung von Escape-Zeichen in text
.
Antwort4
Da Sie eine Perl-Lösung hatten, hier ist eine weitere. Ich verwende rep.txt
zum Speichern des Ersatzes:
$ perl -pe '$re=`cat rep.txt`; chomp($re); s/%SUBSTITUTE%\n/$re/' file.txt
foo
bar
multi
line
string
foo
Dieses liest jede Zeile der Zieldatei ( file.txt
), wendet dann das von angegebene Skript -e
darauf an und druckt ( -p
).