Wie kann ich uniq nur das erste Feld berücksichtigen?

320
Da No

Ich benutze FreeBSD 3.2-RELEASE

Wenn ich einen sortierten Text habe, wie diese lastAusgabe -

zikla13:Oct:20:22:34 zikla13:Oct:5:00:31 zikla14:Oct:17:22:01 zikla14:Oct:12:23:35 zikla14:Oct:12:23:34 zikla14:Oct:12:00:11 zikla14:Oct:11:23:52 zikla14:Oct:5:22:22 zilka13:Oct:13:23:48 zilka13:Oct:11:00:28 zilka13:Oct:9:22:40 

- Gibt es eine Möglichkeit, uniq -cnur das erste Feld zu berücksichtigen (vielleicht mit -s)? In diesem Fall sollte die Ausgabe folgendermaßen sein:

2 zikla13:Oct:20:22:34 6 zikla14:Oct:17:22:01 3 zilka13:Oct:13:23:48 

Oder anders awk?

3
Willkommen bei Super User! Ich habe [zur Bearbeitung Ihrer Frage] (https://superuser.com/review/suggested-edits/455721) zwecks Klarheit und Tag-Relevanz gearbeitet. Bitte beachten Sie, dass diese Site (und [die anderen mögen) (https://stackexchange.com/sites)) sich auf das Stellen und Beantworten von Fragen konzentrieren. Dinge wie „Danke“ in Beiträgen werden zu Gunsten von [Upvoting] (https://superuser.com/help/why-vote) und [Annehmen] (https://superuser.com/help/accepted-answer) hilfreich empfohlen Antworten Blacklight Shining vor 8 Jahren 0
Es gibt mehrere verschiedene Implementierungen von "uniq". Insbesondere unterscheidet sich GNU "uniq" (auf den meisten Linux-basierten Systemen) von dem auf BSDs (einschließlich Mac OS X) gefundenen uniq. Bitte [bearbeiten Sie Ihre Frage] (https://superuser.com/posts/992668/edit), um anzugeben, über welche `uniq`-Implementierung Sie gefragt werden. Blacklight Shining vor 8 Jahren 0

2 Antworten auf die Frage

1
blm

Mit GNU uniq, das die -wOption unterstützt :

$ cat data zikla13:Oct:20:22:34 zikla13:Oct:5:00:31 zikla14:Oct:17:22:01 zikla14:Oct:12:23:35 zikla14:Oct:12:23:34 zikla14:Oct:12:00:11 zikla14:Oct:11:23:52 zikla14:Oct:5:22:22 zilka13:Oct:13:23:48 zilka13:Oct:11:00:28 zilka13:Oct:9:22:40 $ uniq -c -w7 data 2 zikla13:Oct:20:22:34 6 zikla14:Oct:17:22:01 3 zilka13:Oct:13:23:48 

Wie in den Kommentaren darauf hingewiesen, geht man davon aus, dass das erste Feld immer aus sieben Zeichen besteht. Dies ist in Ihrem Beispiel der Fall. Wenn es sich jedoch nicht um das reale Leben handelt, gibt es keine Möglichkeit, es mit uniq zu tun (plus, wenn Sie sich entscheiden) GNU uniq ist -wnicht vorhanden, funktioniert sogar nicht), also hier eine Perl-Lösung:

$ perl -ne '/(.*?):(.*)/;unless (exists $x{$1}){$x{$1}=[0,$2];push @x, $1};$x{$1}[0]++;END[0],$_,$x{$_}[1]) foreach @x}' <data 2 zikla13:Oct:20:22:34 6 zikla14:Oct:17:22:01 3 zilka13:Oct:13:23:48 

So funktioniert das:

$ perl -ne 

Führen Sie perl aus, drucken Sie nicht jede Zeile standardmäßig und verwenden Sie das nächste Argument als Skript.

/(.*?):(.*)/ 

Teilen Sie die Eingabezeile in das Zeug vor dem ersten Doppelpunkt und das Zeug nach dem ersten Doppelpunkt in $1und auf $2. splitwürde auch hier funktionieren.

unless (exists $x{$1}){$x{$1}=[0,$2];push @x, $1} 

Der Hash %xwird verwendet, um die Zeilen und das Array eindeutig zu machen @x, um sie in der richtigen Reihenfolge zu halten (Sie könnten dies auch verwenden sort keys %x, aber Perl sortwird in der gleichen Weise sortiert, wie die Eingabe sortiert wird.) Wenn wir den Strom noch nie gesehen haben "key" (das Zeug vor dem ersten Doppelpunkt), initialisieren Sie einen Hash-Eintrag für die Taste und drücken Sie die Taste auf @x. Der Hash-Eintrag für jeden Schlüssel ist ein Array mit zwei Elementen, das die Anzahl und den ersten Wert nach dem Doppelpunkt enthält. Die Ausgabe kann diesen Wert enthalten.

$x{$1}[0]++ 

Erhöhen Sie die Zählung.

END{ 

Starten Sie einen Block, der ausgeführt wird, nachdem alle Eingaben gelesen wurden.

printf("%8d %s:%s\n",$x{$_}[0],$_,$x{$_}[1]) 

Drucken Sie die Zählung mit Leerzeichen, einem Leerzeichen, dem "Schlüssel", einem Doppelpunkt und dem Zeugs hinter dem Doppelpunkt aus.

foreach @x} 

Tun Sie das für jeden gesehenen Schlüssel in der Reihenfolge und beenden Sie den END-Block.

<data 

Lesen Sie aus der Datei mit dem Namen data im aktuellen Verzeichnis, um die Eingabe zu erhalten. Sie können auch einfach in perl weiterleiten, wenn Sie einen anderen Befehl oder eine andere Pipeline für die Datenerstellung haben.

Dies führt dazu, dass "uniq" nur die ersten sieben Zeichen berücksichtigt. Es funktioniert für das Beispiel des Fragestellers, wird aber wahrscheinlich brechen, wenn das erste Feld nicht genau sieben Zeichen lang ist. Blacklight Shining vor 8 Jahren 0
@ BlacklightShining Guter Punkt. Ich füge eine Perl-Lösung hinzu, die die Zeichen durch: als das Feld für uniq on behandelt, unabhängig von ihrer Länge. blm vor 8 Jahren 0
uniq: unzulässige Option - Ich bedauere, dass mein Fehler im Operator "-w" FreeBSD 3.2-RELEASE - "-w" nicht unterstützt Da No vor 8 Jahren 0
Ja, als Sie hinzugefügt haben, dass Sie FreeBSD verwenden, dachte ich, "-w" würde nicht funktionieren. Ich habe eine Perl-Version hinzugefügt, die jedoch überall funktionieren sollte und nicht darauf angewiesen ist, dass der "Schlüssel" aus 7 Zeichen besteht. blm vor 8 Jahren 0
0
roaima

Ich würde verwenden awk. Filtern und zählen Sie das erste durch Doppelpunkt getrennte Feld, wenn es geändert wird, oder drücken Sie die EOF-Taste, um die gesamte zuvor gespeicherte Zeile zu drucken und zu zählen:

awk -F: '!seen[$1]++ { line[$1]=$0; if(prev); prev=$1} END }' data 

Das awkSkript kann folgendermaßen erweitert werden:

# Count the occurrences of the first field. If first time then... !seen[$1]++ { # save the line line[$1]=$0; # maybe print the previous line if (prev) { printf "%d\t%s\n", seen[prev], line[prev] }; prev=$1 }  # End of file, so print any previous line we have got saved END { if (prev) { printf "%d\t%s\n", seen[prev], line[prev] } } 

Wenn Sie die an awk gelieferten Daten durch Hinzufügen einer nachgestellten Leerzeile ändern können, können Sie auf den gesamten END {...}Block verzichten, den awkCode vereinfachen und die Duplizierung entfernen:

( cat data; echo ) | awk ... 
Sorry aber gesehen [: Event nicht gefunden. Das ist wirklich altes BSD. Ich benutze bash2. Da No vor 8 Jahren 0
@DaNo haben Sie einfache Anführungszeichen um den `awk`-Ausdruck verwendet, wie im Einzeiler gezeigt? roaima vor 8 Jahren 0
Ja, ich kopiere alle Befehle und füge sie in das Terminal ein. . . Da No vor 8 Jahren 0