sappy
Anmeldungsdatum: 24. Januar 2012
Beiträge: 282
|
Hi, ich habe 2 ASCII-Files mit identischen Spalten. Ich will am Ende nur 1 File bekommen. File A, soll Zeile für Zeile durchgegangen werden und ein Wert in einer bestimmten Spalte ("Header2")
überprüft werden, ob dieser Wert auch in File B vorhanden ist. Wenn dies der Fall ist, soll die komplette Zeile in File B gelöscht werden und die von File A genommen werden, ansonsten
soll nichts passieren. Beispiel: File A:
Header1 Header2 Header3
djhask 33 dfsdf
sfsdfc 44 uusdf
ppfgdf 55 hjzjr File B:
Header1 Header2 Header3
sfsdfc 44 zzzzz
2342fr 25 2fsdf
fdsgfg 34 kkds7 File B danach:
Header1 Header2 Header3
2342fr 25 2fsdf
fdsgfg 34 kkds7
sfsdfc 44 uusdf Ich habe eine Idee und es klappt auch, jedoch dauert das sehr sehr lange (habe 150.000 Zeilen!): | sed 1d fileA.dat | while read line; do
as=$(echo "$line" | awk -F ' ' '{print $2}')
a=$(grep $as fileA.dat)
if grep -q $as fileB.dat; then
sed -i "/$as/d" ./fileB.dat
sed -i "$ a $a" fileB.dat; fi
done
|
Kann ich das irgendwie optimieren? LG
sappy
|
user_unknown
Anmeldungsdatum: 10. August 2005
Beiträge: 17552
Wohnort: Berlin
|
Nennen wir Spalte 2 den Schlüssel. Beide Dateien haben je 150.000 Zeilen? Und ein Schlüssel kann mehrfach vorkommen, in beiden Dateien? Da Du 2stellige Zahlen als Beispiel verwendest stellt sich die Frage. Und die Dateien sind nicht nach dem Schlüssel sortiert. Dürfte man sie umsortieren? Ansonsten sieht es bei 150.000² Vergleichen hart aus. Wenn Du den Dateien ein zusätzliches Feld 'original Zeilennummer' spendierst, sie dann beide nach dem Schlüssel sortierst, dann kannst Du immer aus der Datei mit dem kleineren Schlüssel die nächste Zeile lesen. Sind beide Schlüssel gleich, liest Du aus beiden Dateien. Schreibst aber aus Datei B immer die Originalzeilennummer. Am Ende sortierst Du wieder mit der Originalzeilennummer und schmeißt diese danach weg.
|
sappy
(Themenstarter)
Anmeldungsdatum: 24. Januar 2012
Beiträge: 282
|
Zu den Fragen: - beide Files haben eine unterschiedliche Anzahl an Zeilen
- ein Schlüssel kann entweder in File A vorkommen oder in File B oder in beiden Files
- das 2 stellige ist ein Beispiel ... in echt ist der Schlüssel wesentlich länger (Buchstaben/Zahlen-Kombination)
- sortiert nach Schlüssel sind sie nicht, könnte man aber machen
LG
sappy
|
track
Anmeldungsdatum: 26. Juni 2008
Beiträge: 7174
Wohnort: Wolfen (S-A)
|
Lese ich das richtig, dass die Spalten durch Leerzeichen getrennt sind, und dass innerhalb der Spalten aber keine Leerzeichen vorkommen ? In dem Fall ginge das recht hübsch mit einem assoziativen Array, z.B. unter awk . Du müsstest von der Datei A alle Zeilen in das Array einlesen, mit "Wort" 2 als Index. Bei Datei B guckst Du dann, ob es eine Zeile mit diesem Index gibt. Wenn ja, nimmst Du sie, wenn nein, gibst Du einfach die Originalzeile aus: track@track:~$ awk 'ARGIND==1 {a[$2]= $0} ARGIND==2 {if(a[$2]) print a[$2]; else print}' datei_a datei_b
Header1 Header2 Header3
sfsdfc 44 uusdf
2342fr 25 2fsdf
fdsgfg 34 kkds7 LG, track
|
user_unknown
Anmeldungsdatum: 10. August 2005
Beiträge: 17552
Wohnort: Berlin
|
sappy schrieb: Zu den Fragen: - beide Files haben eine unterschiedliche Anzahl an Zeilen
(Listen muss man nicht noch in einen Codeblock setzen.) Aber es ist nicht eine wesentlich kürzer als die andere?
- ein Schlüssel kann entweder in File A vorkommen oder in File B oder in beiden Files
Kann maximal 1x in Datei A oder B oder beiden vorkommen?
- das 2 stellige ist ein Beispiel ... in echt ist der Schlüssel wesentlich länger (Buchstaben/Zahlen-Kombination)
- sortiert nach Schlüssel sind sie nicht, könnte man aber machen
Dann würde ich das machen. Dann kannst Du tip-tip-tip-tap-tip-tap-tap-tip-tip durch sie durchgehen, bei je 150k Zeilen sind das 300k Schritte, nicht 22.500M Schritte.
|
sappy
(Themenstarter)
Anmeldungsdatum: 24. Januar 2012
Beiträge: 282
|
Hi track, danke - das klappt prima. Kann ich einen gleichen Durchlauf danach nochmal starten, wo ich alle Schlüssel-Zeilen von File A nach B schreibe, weil es die in File B noch nicht gibt?
Ginge das irgendwie so? | awk 'ARGIND==1 {a[$2]= $0} ARGIND==2 {if ! (a[$2]) print a[$2]}' datei_a datei_b
|
@user unknown: Aber es ist nicht eine wesentlich kürzer als die andere?
- beide sind in etwa gleich lang Kann maximal 1x in Datei A oder B oder beiden vorkommen?
- Puh, das kann ich nicht zweifelsfrei sagen - noch nicht gecheckt lg
sappy
|
user_unknown
Anmeldungsdatum: 10. August 2005
Beiträge: 17552
Wohnort: Berlin
|
So kannst Du prüfen, ob ein Schlüssel mehrfach vorkommt:
| awk '{print $2}' A | sort | uniq -c | grep -v "^ *1 "
|
Mit diff B B2 kannst Du Aktualierungen von B extrahieren, wenn die Datei nicht umgewürfelt wird, sondern nur Zeilen verschwinden, Teile des Inhalts ändern oder hinzukommen. Paramter für diff bitte selbst ansehen. Als 2. Datei würdest Du dann das diff verwenden.
|
user_unknown
Anmeldungsdatum: 10. August 2005
Beiträge: 17552
Wohnort: Berlin
|
Wenn Du die Dateien beliebig umsortieren kannst, dann kannst Du auch mit join arbeiten.
| > cat A
djhask 33 dfsdf
sfsdfc 44 uusdf
ppfgdf 55 hjzjr
foobar 49 asdf
> cat B
sfsdfc 44 zzzzz
2342fr 25 2fsdf
fdsgfg 34 kkds7
|
Sortieren:
| sort -k 2 A > a
sort -k 2 B > b
|
Join über Feld 2: | join -j 2 a b
44 sfsdfc uusdf sfsdfc zzzzz
# bzw.
join -j 2 a b -o 2.1,2.2,2.3
sfsdfc 44 zzzzz
|
Aber tracks Lösung ist ja schon übersichtlich, elegant und offenbar schnell genug. Mit dem join-Output wärst Du ja noch nicht fertig und ob das schneller ist, ist wohl zu bezweifeln.
|
track
Anmeldungsdatum: 26. Juni 2008
Beiträge: 7174
Wohnort: Wolfen (S-A)
|
sappy schrieb: ... Kann ich einen gleichen Durchlauf danach nochmal starten, wo ich alle Schlüssel-Zeilen von File A nach B schreibe, weil es die in File B noch nicht gibt?
Ehrlich gesagt verstehe ich Deine Fragestellung nicht ganz. Beschreib doch mal, wie Du das mit Hand machen würdest, oder zeig mal, wie das Ergebnis mit Deinen Beispiel-Dateien aussähe. Aber in der Tendenz würde ich solch eine Nebenaufgabe gleich mit in das awk-Skript oben mit einbauen, dann braucht man den Kram nicht mehrfach zu lesen. (m.a.W.: es läuft schneller) LG, track p.s.: Du solltest Deine Fragen dazu an mich bald stellen, denn ab morgen Abend bin ich für gut 1 Woche im Urlaub ! - ohne Netz natürlich.
|
sappy
(Themenstarter)
Anmeldungsdatum: 24. Januar 2012
Beiträge: 282
|
Hi track, danke für den Hinweis.
Ich hab das kompliziert ausgedrückt irgendwie. Im Grunde kann man es ganz einfach beschreiben:
wir haben File A und File B ALLE Zeilen von File A sollen in File B eingepflegt werden Bedingung aber: wenn der Schlüssel von File A (dieses Feld/Spalte2) auch schon in File B drin stand, dann soll er die Datenzeile in B löschen und die Zeile von A nehmen
manuelles Beispiel (vereinfacht, zur Veranschaulichung):
File A:
Header1 Header2 Header3
AAAAAAA 1111111 uusd43f
BBBBBBB 2222222 6hz34rg
CCCCCCC 3333333 kljcv87
DDDDDDD 4444444 890dfch
File B:
Header1 Header2 Header3
EEEEEEE 5555555 ncjk072
FFFFFFF 2222222 432hdfe
GGGGGGG 7777777 nxvbc72
HHHHHHH 8888888 xx52hss
Final:
EEEEEEE 5555555 ncjk072
GGGGGGG 7777777 nxvbc72
HHHHHHH 8888888 xx52hss
AAAAAAA 1111111 uusd43f
BBBBBBB 2222222 6hz34rg
CCCCCCC 3333333 kljcv87
DDDDDDD 4444444 890dfch Final sollte also zuerst einmal File B alle Zeilen beinhalten. Jetzt pflegen wir File A in B ein:
Dazu wird Zeile mit dem Schlüssel 111.. übernommen, weil die ja in B fehlt. Bei der nächsten Zeile wird es
interessant. Schlüssel 222... gibt es schon in File B. Also löschen wir die Zeile in B und übernehmen die aus File A.
Die Zeilen aus File A mit Schlüssel 333... und 444.. werden wieder in File B eingepflegt, da sie ja fehlen.
Damit wäre das finale File fertig. Hab schon überlegt ob es nicht einfach besser wäre, die 2 Files erst zu concatten und dann nach -k2 zu sorten. | cat fileA > 1
cat fileB >> 1
sort -u -k 2 1 > fertig
|
Frage hier ist: welche Zeile behält das uniq? Die von File A oder File B? Sieht aus als die von A. Dann würde das ja klappen... lg
sappy
|
user_unknown
Anmeldungsdatum: 10. August 2005
Beiträge: 17552
Wohnort: Berlin
|
Wieso testest Du es nicht mit Deinen Beispielfiles?
|
track
Anmeldungsdatum: 26. Juni 2008
Beiträge: 7174
Wohnort: Wolfen (S-A)
|
Ok, bei Deinem Vorhaben könnte man ja mit meinem awk - Filter oben erstmal alle Zeilen in Datei B ersetzen, wie gehabt. Wenn Du jede Zeile, die Du schon übernommen hast, dabei gleich löscht, bleiben in dem Array ja nur die übrig, die noch nicht vorgekommen sind. Dann würde es ja theoretisch reichen, wenn Du den Rest dann einfach am Ende auch noch ausgibst. Dann hast Du das zwar in einer "wilden" Reihenfolge angehängt, aber immerhin: track@track:~$ awk 'ARGIND==1 {a[$2]= $0} ARGIND==2 {x=0; if(a[$2]) {print a[$2]; delete a[$2]} else print} END {for(i in a) print a[i]}' datei_a datei_b
Header1 Header2 Header3
sfsdfc 44 uusdf
2342fr 25 2fsdf
fdsgfg 34 kkds7
ppfgdf 55 hjzjr
djhask 33 dfsdf Das funktioniert natürlich nur dann sauber, wenn nirgends ein Index doppelt vorkommt ...
|
sappy
(Themenstarter)
Anmeldungsdatum: 24. Januar 2012
Beiträge: 282
|
Hi track, hervoragend! Leider haut er mir an einigen Stellen Leerzeilen rein. Wie kommt das? lg
sappy
|
track
Anmeldungsdatum: 26. Juni 2008
Beiträge: 7174
Wohnort: Wolfen (S-A)
|
Wird ne Macke von awk sein ... Eigentlich sollten die gelöschten Array-Felder wirklich weg sein (auch lt. Doku), aber aus irgend einem Grund sind die danach nur leer und nicht wirklich weg. Aber gut, Du kannst die leeren Zeilen in dem Fall ja im END - Block einfach übergehen, genau wie vorher oben: END {for(i in a) if(a[i]) print a[i]} Ist nicht ganz die saubere Art, behebt aber das Problem. 😀 LG, track p.s.: ich bin dann jetzt tatsächlich weg, Mo. in 1 Woche. Bis dahin -
|
sappy
(Themenstarter)
Anmeldungsdatum: 24. Januar 2012
Beiträge: 282
|
Super, danke! Na dann hast dir dein Urlaub aber nun wirklich verdient 😉
Erhol dich gut und bis bald! lg sappy
|