PDA

Archiv verlassen und diese Seite im Standarddesign anzeigen : cat, cut, grep, sort und uniq -> kriege es nicht hin


TV-Kucker
2007-03-08, 01:40:56
Ich versuche hier seit Stunden doppelte Einträge aus einer Liste zu entfernen und kriege es nicht gebacken. Entfernt werden soll die 2. oder 3. Zeile. Entweder anhand der ersten oder (besser) anhand der letzten Spalte. Die neue Liste soll in einer neuen Datei gespeichert werden.

Auszug aus der Datei:
ATV+:12728:v:0:22000:506:507:13012
AXN:12320:v:0:27500:2030:2031:13203
AXN:12324:v:0:27500:2030:2031:13203
Bahn TV:12662:h:0:22000:201:301:12600
Bayerisches FS:11875:h:0:27500:201:202:28107

Gnafoo
2007-03-08, 03:09:31
Doppelte VDR-Kanäle? Ich kann dir ein Ruby-Script anbieten. Ka wie man das mit uniq&co hinbekommt.


File.open('channels.conf', 'r') do |file|
entries = {}; count = 0

while line = file.gets do
id = line.split(':').last
entries[id] = [count += 1,line] unless entries.has_key?(id)
end

puts entries.values.sort.map { |e| e[1] }
end


Geht sicher noch schöner, aber macht was es soll und die Reihenfolge der Einträge bleibt auch erhalten. Ruby-Interpreter installieren, channels.conf ins selbe Verzeichnis und mit:


ruby prog.rb > channels.conf.new


aufrufen. Wenn du kein Ruby verwenden kannst/willst, musst du schauen, wie du das am besten mit einer anderen Skriptsprache löst :D

ThePsycho
2007-03-08, 14:01:10
Die genannten Tools bieten sich hier imho nicht an, da du einen Teil aus der Zeile schneiden musst, vergleichen und anhand dieses Ergebnisses die ursprüngliche(!) Liste bearbeiten willst.

Hier wäre vll awk noch eine Möglichkeit (ungetestet):


awk -F":" '
{
if (index[$NF]==0) {
print $0
index[$NF]=1
}
}
' alte_datei > neue_datei

Bei erster Spalte alternativ "$1" statt "$NF".

TV-Kucker
2007-03-08, 14:39:39
@Der Tod
Ja, ist für eine channels.conf. Ich kann ein bisschen PHP. Damit könnte man das natürlich lösen. Btw die Ruby Syntax liegt mir auf den ersten Blick gar nicht.


@Rest
Kennt sich niemand hier mit der Linux Shell aus (herausfordener Blick)? Bis jetzt bin ich so weit:

cut -d: -f 1 bla.txt | sort | uniq | nl

-> Liefert eine Liste mit 216 einzigartigen Sendernamen. Das Problem ist, das ich die nächsten 7 Spalten auch brauche!!!


@ThePsycho
Jetzt hätte ich fast dein Posting übersehen. Die awk-Lösung muss ich mir nachher noch mal genauer anschauen. Momentan verstehe ich nicht, wie man das anwendet.

Gnafoo
2007-03-08, 15:02:10
Geht nicht sowas ähnliches?


#!/bin/sh
for name in `cut -d: -f 1 bla.txt | sort | uniq | nl` do
cat channels.conf | grep $name | head -n 1 >> channels.new.conf
done

TV-Kucker
2007-03-08, 17:05:58
Ich muß vorweg anmerken, dass ich mich erst seit einem Monat mit Linux beschäftige und nicht wirklich Ahnung habe.


Die Kombination von grep und head ist eine sehr gute Idee. Das müßte funktionieren. Allerdings tut es momentan genau das Gegenteil.
Von den 275 Einträgen in "bla.txt" sind 216 einzigartig. Die neu erstellte Datei hat allerdings 355 Zeilen. Um den Fehler zu finden, habe das Script etwas verändert:


#!/bin/sh
for name in `cut -d: -f 1 bla.txt | sort | uniq`
do
echo $name >> sender.txt
cat bla.txt | grep $name | head -n 1 >> bla.new.txt
done


Auszug aus sender.txt:
WDR
Aachen
WDR
Bonn
WDR
Duisburg

Richtig wäre:
WDR Aachen
WDR Bonn
WDR Duisburg

Es sieht für mich nach einen Problem mit Leerzeichen aus. Wie kann man das beheben?



PS: Der einzelne Befehl im Terminal sieht richtig aus:

$ cut -d: -f 1 bla.txt | sort | uniq
...
...
WDR Aachen
WDR Bonn
WDR Duisburg
...

ThePsycho
2007-03-08, 17:15:25
Alle Lösungen nach meinem Posting gehen nach der ersten Spalte - das wollte ich noch anmerken.

Was die Funktionsweise von meinem awk-Programm angeht: Das läuft so:

Er wendet für jede Zeile, die er in der Eingabedatei findet, das, was er zwischen { und } findet an.
In diesem Falle schaut er, ob im Array "index" ein Wert mit dem Index <letztes Feld> gleich 0 findet (unbenutzte, neue Variablen haben in awk immer den Wert 0)
Falls nicht, ist der Sender neu und die Zeile wird auch in die neue Datei geschrieben (print $0) - danach eben noch das Array entsprechend umsetzen.

Du kannst natürlich auch die Daten reinpipen, falls es dir lieber ist:


cat alte_datei | awk -F":" ' ... ' > neue_datei


Warum ich hier keine Shellkommandos nehmen würde, habe ich ja schon geschrieben...

ThePsycho
2007-03-08, 17:20:38
#!/bin/sh
for name in `cut -d: -f 1 bla.txt | sort | uniq`
do
echo $name >> sender.txt
cat bla.txt | grep $name | head -n 1 >> bla.new.txt
done


Auszug aus sender.txt:
WDR
Aachen
WDR
Bonn
WDR
Duisburg

Richtig wäre:
WDR Aachen
WDR Bonn
WDR Duisburg

Es sieht für mich nach einen Problem mit Leerzeichen aus. Wie kann man das beheben?

In bash (bei anderen Shells ist das anders!) greift die for-Schleife auf die Variable "IFS" zurück und trennt nach deren Regeln das Array auf - im Standardfall nach Leerzeichen und Zeilenumbrüchen!
Das `cut -d: -f 1 bla.txt | sort | uniq` ist also richtig, das Problem ist die for-Schleife.

Du kannst das durch umsetzen der IFS-Variablen erreichen:
IFS=$'\n' (Standard ist: IFS=$' \t\n')
(einfach vor der Schleife in das Skript einfügen - ein "export" solltest du dir aber verkneifen!)

edit: Ich glaube du mußt IFS="$'\n'" schreiben oder mit \ escapen: IFS=\$'\\n' - probier mal etwas rum, Sonderzeichen sind halt etwas tricky...

TV-Kucker
2007-03-08, 17:24:01
Ahhh, ich bin so dämlich! Wenn ich die Zahl am Ende der Zeile nehme funktioniert es natürlich. Dort sind keine Leerzeichen drin.
-> `cut -d: -f 1 bla.txt | sort | uniq` ersetzen durch `cut -d: -f 8 bla.txt | sort | uniq`


Die Antwort auf die Frage aus #6 würde mich dennoch interessieren!

edit: man seit ihr schnell! Vielen Dank!

ThePsycho
2007-03-08, 17:57:43
#6 wurde doch mit #8 beantwortet?

Oder was genau meinst du?

TV-Kucker
2007-03-08, 18:06:58
@ThePsycho
Ja, - und zwar schneller als ich tippen konnte. Ich werde es nachher noch mal genau testen. Danke.

-Phoenix-
2007-03-08, 18:26:26
bleibt nur noch eins zu sagen: www.seidseit.de ;)

Gast
2007-03-08, 22:05:01
Da du uniq nutzt, nehme ich an, daß gleiche Zeilen immer aufeinanderfolgen. Dann tuts auch ein regulärer Ausdruck:

sed '$!N; /^.\+\(:.\+\)\n.\+\1$/!P; D;' <datei>

Viele der umgedrehten Schägstriche sind nur nötig, damit sich die Shell nicht angesprochen fühlt...

Grundsätzlich kann man das Problem mit jeder Programmier- oder Skriptsprache lösen, auch als Shell-Skript. Für einfache Textmanipulationen sind Perl und reguläre Ausdrücke jedoch sehr effizient.
#!/usr/bin/perl -w
use strict;
open (FILE, "< $ARGV[0]") || die "$ARGV[0]: $!\n";
read (FILE, my $file, -s $ARGV[0]);
close FILE;
$file =~ s/^(.+(:.+))\n.+\2$/$1/mg;
print $file;
Als etwas aufgeblähtes Perlskript ist es auch nicht mehr so verwirrend wie der sed-Einzeiler oben.

Falls die gleichen Zeilen nicht aufeinanderfolgen müssen sie entweder vorher sortiert werden oder man benutzt einen Hash, wie in der Lösung von ThePsycho.

instinct
2007-03-10, 15:13:38
zum thema doppelte eintrage rauswerfen koenntest du auch "sort -u" nehmen, wird sortiert, doppelte eintraeg rausgeworfen und du kannast dann weiter arbeiten ...

Ajax
2007-05-01, 20:18:31
Spam-Bots bitte melden. Nicht kommentieren. Danke!

Wegen jener welchen geschlossen. :-/