Ich möchte mit sed einen neuen String in die letzte Zeile einfügen, beginnend mit etwas Text,

Ich möchte mit sed einen neuen String in die letzte Zeile einfügen, beginnend mit etwas Text,

Durchsucht die Datei nach der letzten Zeile mit einem bestimmten String, um eine Zeile nach dieser Übereinstimmung hinzuzufügen. Beispiel:

Eingang:

Apple
Banana
Apple
Orange
Apple
Grapefruit

Ausgabe:

Apple
Banana
Apple
Orange
Apple
I am new string replaced at last apple
Grapefruit

Antwort1

Mit GNU sedkönnen Sie Folgendes tun:

sed ':a;N;$! ba;s/.*\nApple/&\nYour appended line/'

Das Muster :a;N;$! bahängt Zeilen aus der Datei mit an den Puffer an, Nsolange das Dateiende ( $Adresse) nicht ( !) erreicht ist (Sprung zu :amit ba). Wenn du also die Schleife verlässt, sind alle Zeilen im Puffer und das Suchmuster .*\nApplepasst in die ganze Datei bis zur letzten Zeile, die mit beginnt Apple. Dann &fügt der in den Ersetzungsstring die ganze Übereinstimmung ein und hängt deinen Text an.

Eine portable Lösung, die sedauch auf anderen Versionen funktioniert, wäre

sed -e ':a' -e 'N;$! ba' -e 's/.*\(\n\)Apple/&\1Your appended line/'

Hier wird das Skript an den Labels aufgeteilt und statt \nim Ersetzungsstring zu verwenden (was bei Nicht-GNU- seds nicht garantiert funktioniert), wird auf den aus dem Muster verwiesen, das \(\)mit der \1Referenz umgeben ist.

Antwort2

Ich habe dieses Beispiel gesehenHier

sed -i -e "\$aTEXTTOEND" <filename>

Information:

i: edit files in place    
e: add the script to the commands to be executed    
$: sed address location
a: append command
TEXTTOEND: text to append to end of file

Antwort3

Ich gehe davon aus, dass Ihre Beispieleingabe eine Datei mit dem Namen ist fruits.txt.


Möchte man nach dem letzten Vorkommen von „Apple“ eine neue Zeile einfügen, geht das sedetwa so:

sed -rz 's/(^(.*\n)?Apple)(\n|$)/\1\ninserted line\n/'

Die -rOption aktiviert erweiterte reguläre Ausdrücke.
Die -zOption weist darauf hin sed, dass als Zeilentrennzeichen NUL-Zeichen (ASCII-Code 0) anstelle der normalen Zeilenumbruchzeichen verwendet werden sollen. Auf diese Weise wird die gesamte Textdatei auf einmal verarbeitet, anstatt Zeile für Zeile.

Der sedBefehl s/PATTERN/REPLACEMENT/sucht in nach dem (erweiterten -r) regulären Ausdruck PATTERNund ersetzt ihn durch den ausgewerteten REPLACEMENTString.

Der reguläre Ausdruck (^(.*\n)?Apple)(\n|$)passt zu einer beliebigen Anzahl von Zeilen vom Anfang ( ^) bis zum letzten Vorkommen von, Applegefolgt von einem Zeilenumbruch ( \n) oder dem Dateiende ( $).

Jetzt wird diese ganze Übereinstimmung durch ersetzt \1\ninserted line\n, wobei dies \1für den ursprünglichen Inhalt der ersten Übereinstimmungsgruppe steht, also alles bis Apple. Tatsächlich wird also direkt nach dem letzten Vorkommen von „Apple“ inserted lineein Zeilenumbruch ( ) eingefügt, der vorangestellt und nachgestellt wird .\n

Dies funktioniert auch, wenn die Zeile „Apple“ die erste, letzte oder einzige Zeile in der Datei ist.

Beispiel-Eingabedatei (Ihrem Beispiel wurde eine Zeile hinzugefügt, um das Verhalten deutlich zu machen):

Apple
Banana
Apple
Orange
Apple
Cherry

Beispielausgabe:

Apple
Banana
Apple
Orange
Apple
inserted line
Cherry

Wenn Sie mit dem Ergebnis zufrieden sind und die Änderungen direkt vor Ort vornehmen möchten fruits.txt, anstatt nur die Änderungen auszugeben, können Sie die folgende -iOption hinzufügen:

sed -irz 's/(^(.*\n)?Apple)(\n|$)/\1\ninserted line\n/'

Wenn Sie jedoch lediglich eine Textzeile an das Ende der Datei anhängen möchten, sedist dies nicht das optimale Tool.

>>Sie können dann einfach die Umleitung der angehängten Ausgabe von Bash verwenden :

echo "I am new string replaced at last apple" >> fruits.txt

Das >>nimmt die Standardausgabe des Befehls auf der linken Seite (der echoBefehl druckt hier einfach seine Argumente auf die Standardausgabe) und hängt sie an die Datei an, die auf der rechten Seite angegeben ist. Wenn die Datei noch nicht existiert, wird sie erstellt.

Antwort4

In dieser Antwort:

  1. sed+tac (doppelte Eingabeumkehr)
  2. AWK mit Doppeleingang
  3. Perl mit doppelter Eingabe
  4. Alternatives Perl mit doppelter Eingabeumkehr

sed + tac

Bei diesem Ansatz kommen drei Prozesse zum Einsatz: Zuerst wird die Datei umgekehrt, dann wird sed verwendet, um die erste Übereinstimmung zu finden, der gewünschte Text wird nach der ersten Übereinstimmung eingefügt und dann wird der Text erneut umgekehrt.

$ tac input.txt | sed '0,/Apple/{s/Apple/NEW THING\n&/}' | tac 
Apple
Banana
Apple
Orange
Apple
NEW THING
something else
something other entirely
blah

Die Grundidee ist dieselbe wie bei der unten beschriebenen alternativen Perl-Version: Die erste Übereinstimmung in der umgekehrten Zeilenliste ist die letzte Übereinstimmung in der ursprünglichen Liste. Der Vorteil dieses Ansatzes ist Einfachheit und Lesbarkeit für den Benutzer.

Dieser Ansatz funktioniert gut bei kleinen Dateien, ist jedoch bei großen Dateien umständlich und benötigt wahrscheinlich eine beträchtliche Zeit, um eine große Anzahl von Zeilen zu verarbeiten.

AWK

Awk kann für das, was Sie versuchen, ein wenig benutzerfreundlicher sein:

$ awk -v n="AFTER LAST APPLE" 'NR==FNR&&/Apple/{l=NR};NR!=FNR{if(FNR==l){print $0;print n}else print}' input.txt input.t>
Apple
Banana
Apple
Orange
Apple
AFTER LAST APPLE
something else
something other entirely
blah

Die Grundidee besteht darin, Ihre Eingabedatei zweimal an awk zu übergeben – zunächst, um herauszufinden, wo sich der letzte Apfel befindet, und dann, um den Text einzufügen, wenn wir die Position des letzten „Apfels“ übergeben, den wir bei der ersten Iteration gefunden haben.

Der Schlüssel zum Erfolg liegt in der Auswertung der Variablen NR(wobei alle verarbeiteten Zeilen und Summen gezählt werden) und FNR(Zeilennummer in der aktuellen Datei). Wenn diese beiden Dateien ungleich sind, wissen wir, dass wir die zweite Datei verarbeiten. Somit wissen wir, dass wir den ersten Versuch unternommen haben, den Speicherort des letzten Vorkommens von Apple zu finden.

Da wir die Datei zweimal lesen, hängt die Leistung von der Größe der Datei ab, ist aber besser als die Kombination aus sed und tac, da wir die Datei nicht zweimal umkehren müssen.

Perl

perl -sne '$t+=1;$l=$. if /Apple/ and $.==$t;
          if($t!=$.){if($.==$l){print $_ . $n . "\n";}else{print $_}};
          close ARGV if eof;' -- -n="AFTER LAST APPLE" input.txt input.txt

Die Perl-Lösung folgt derselben Idee wie die AWK-Lösung. Wir übergeben die Eingabe zweimal und verwenden die erste Dateiübergabe, um herauszufinden, wo Apple zuletzt vorkam, und speichern die Zeilennummer in einer $lVariable. Der Unterschied zu AWK besteht darin, dass es FNRin Perl keine Variable gibt, daher müssen wir zu diesem Zweck $t+=1und verwenden close ARGV if eof.

Der Unterschied besteht darin, dass -sPerl mit switch die Übergabe von Argumenten erlaubt. In diesem Fall wird der String, den wir einfügen möchten, über -nswitch übergeben.

Alternative Perl

Hier ist noch eine weitere Möglichkeit, dies in Perl zu tun. Die Idee besteht darin, die Datei einfach in ein Zeilenarray einzulesen und dieses Array umzukehren. Die erste Übereinstimmung im umgekehrten Array ist die letzte in der ursprünglichen Zeilenliste. Daher können wir vor dieser Zeile einen gewünschten Text einfügen und die Liste erneut umkehren:

$ perl -le '@a1=<>;END{do{push @a2,"NEW LINE\n" and $f=1 if $_ =~ /Apple/ and !$f; push @a2,$_} for reverse(@a1); print reverse(@a2)}' input.txt      
Apple
Banana
Apple
Orange
Apple
NEW LINE
something else
something other entirely
blah

verwandte Informationen