[sed] eine (einzige) Zeile einfügen, wenn keine Zeile dem Suchmuster entspricht

rumbalotte

Lieutenant
Registriert
Okt. 2010
Beiträge
668
Servus!

Ich habe ein "Problem", welches ich hoffe, mit sed (dem streamline editor) zu lösen zu können.
Ich habe eine Textdatei mit Produktdaten, dazu kommen regelmäßig Delta-Updates; diese können
  • neue
  • zu aktualisierende
  • zu löschende
Zeilen enthalten.
Die letzten beiden sind überschaubar, der erste Fall ist interessanter:
wenn ich meinen sed-Befehl "füge Zeile ein, wenn Suchmuster nicht erfüllt ist" anwende, dann würde das ja auf jede Zeile angewendet werden, da neu, nirgends gefunden werden kann. Ich will besagte neue Zeile aber nur ein einziges Mal hinzufügen :)
Hat da jemand einen Ansatz, wie (und ob?) das mit sed zu bewerkstelligen ist?

Danke!
Die Lotte
 
Mit sed wüsste ich nicht. Normales Bash-Scripting tut's aber auch :)
Code:
if [ $(egrep -c "produkt-id" dateiliste.txt) -eq 0 ]; then
        echo "nix gfunden. hinten anfügen!" >> dateiliste.txt
fi
 
Code:
awk '/produkt-id/{f=1}END{ if (!f) {print "noMatch"}}1' dateillist.txt

sollte es auch tun...

Mit sed ist es... gebastel, viel gebastel
 
Ich blicke nicht ganz durch, ist die Position der einzigen hinzugefügten Zeile entscheidend? Dürfen die Zeilen sortiert werden? Wie genau sieht der "füge ein, wenn Suchmuster nicht erfüllt ist"-Befehl aus? sed wird schnell hässlich, wenn es um Duplikate geht, deswegen hier zwei Lösungen mit und ohne Sortierung:
Code:
$ cat test
eintrag1
32
Bc-2
32
neu
installieren
Bc-2

$ sort -u test
32
Bc-2
eintrag1
installieren
neu

$ awk '!x[$0]++' test      
eintrag1
32
Bc-2
neu
installieren
 
eine Info hatte ich vergessen:
Ich bekomme im Delta leider nur die Markierung "delete" oder "update", "update" kann sowohl ein neuer Datensatz als auch ein bereits vorhandener sein - deswegen das Trara, um überhaupt festzustellen, ob es ein neuer Datensatz wäre (sonst könnte mans ja einfach concatten).


@klenamenis
für das aktualisieren einer Zeile wär's z.B.
Code:
sed '/^"724389"/c <neue Zeile>' original.csv
für's löschen einfach
Code:
sed '/^"724389"/d' original.csv

aber a(ppend) würde ja - wie schon beschrieben - letztendlich bei jeder Zeile anspringen, wenn das Suchmuster nicht erfüllt sein soll

Da es sich hier um knapp 5Mio Datensätze handelt, dürfte ein Sortieren eher kontraproduktiv sein :D

Mit sed ist es... gebastel, viel gebastel
sed wird schnell hässlich, wenn es um Duplikate geht
ja, den Eindruck hatte ich bisher auch ;)

neuer Ansatz:
Beim Einlesen der Originaldatei werden jetzt einzelne Dateien erzeugt, welche z.B. alle Produkte, deren ID mit 10, 11, 12, ..., 73, ..., 98, 99 anfangen enthalten, zusätzlich wird ein HASH (Perl) angelegt, welcher alle IDs enthält (den ich abspeicher und später wieder zur Verfügung habe).
Über den HASH kann ich feststellen, ob ich eine ID schon habe und weiß, wie ich die neue / zu aktualisierende Zeile behandeln muss; aufgrund der vielen einzelnen Dateien kann sed wesentlich performanter arbeiten.

Da es eben so viele Daten sind, kann ich das auch nicht mal einfach so alles mit Perl einlesen; das kann zwar gut mit riesigen Datenmengen umgehen, aber das würde es auch an die Grenzen bringen :)
 
Code:
sed -n 'p;/<suchbegriff>/h;${x;/^$/a<insert if not found>
}' test.txt

funktioniert ausser wenn das file leer ist...
 
sed lässt sich auf einen Zeilenbereich der Eingabe beschränken, den du vorher mittels grep bestimmen könntest.
Code:
$ cat test
eintrag1
32
Bc-2
32
neu
installieren
Bc-2

# -v: invert-match, -n: zeige Zeilennr., -m N: max. N Treffer
$ grep -v -n -m 1 "eintrag1" test
2:32

$ grep -vnm1 "eintrag1" test | cut -d: -f1
2
Tauscht du "eintrag1" und test durch gewünschte Ausdrücke und Dateien aus, erhälst du mit letztem Befehl die Zeile, die als erstes nicht dem Muster entspricht. sed kann nun einzig diese Zeile betrachten:
Code:
$ row=`grep -vnm1 "AUSDRUCK" DATEI | cut -d: -f1`

$ sed '$row a EINSCHUB' DATEI

grep ließe sich sogar über GNUs parallel auf mehrere Kerne auslegen, sollte das bei deinen großen Dateien von merklichem Vorteil sein.

Push: Ich bin neugierig, (wie) hast du es nun gelöst?
 
Zuletzt bearbeitet von einem Moderator:
Sorry für die lange antwortzeit :(
Wir haben uns jetzt für eine Redis Instanz entschieden.
RAM ist auf dem System kein Problem und das schlägt einfach jeden Dateizugriff um Längen.
Da muss man auch nicht umständlich schauen, ob es einen Eintrag schon gibt - gibt es unter der (z.B.) ProduktID schon einen Eintrag, wird dieser einfach aktualisiert, ansonsten angelegt, ein Delete ist auch ein einziger Befehl
Und da aus der Ausgangsdatei sowieso anders strukturiere Dateien erzeugt werden müssen, ist es relativ gleich, ob die Quelle aus einer Datei oder einer Redis DB kommt (das nimmt sich auch nichts an Performance, weil das Schreiben der größte Aufwand ist).


So oder so - Danke für die Tips und Anregungen!
Einiges kann ich bestimmt auch woanders verwenden, das war ja nicht das letzte Projekt, wo wir Daten verarbeiten müssen. Und auch so hab ich wieder was über sed & grep gelernt - das ist ja uferlos, was da alles geht ;)
Es kommt auf jeden Fall alles in meine "Codesnippets, die ich nicht vergessen will"-Sammlung :)
 
Zurück
Oben