Versucht, nach zwei Feldern zu sortieren, dann nach den ersten

106

Ich versuche nach mehreren Spalten zu sortieren. Die Ergebnisse sind nicht wie erwartet.

Hier sind meine Daten (people.txt):

Simon Strange 62
Pete Brown 37
Mark Brown 46
Stefan Heinz 52
Tony Bedford 50
John Strange 51
Fred Bloggs 22
James Bedford 21
Emily Bedford 18
Ana Villamor 44
Alice Villamor 50
Francis Chepstow 56

Folgendes funktioniert ordnungsgemäß:

bash-3.2$ sort -k2 -k3 <people.txt                                                                                                                    
Emily Bedford 18                                                                                                                                      
James Bedford 21                                                                                                                                      
Tony Bedford 50                                                                                                                                       
Fred Bloggs 22                                                                                                                                        
Pete Brown 37                                                                                                                                         
Mark Brown 46                                                                                                                                         
Francis Chepstow 56                                                                                                                                   
Stefan Heinz 52                                                                                                                                       
John Strange 51                                                                                                                                       
Simon Strange 62                                                                                                                                      
Ana Villamor 44                                                                                                                                       
Alice Villamor 50

Folgendes funktioniert jedoch nicht wie erwartet:

bash-3.2$ sort -k2 -k1 <people.txt                                        
Emily Bedford 18                                                                                                                                      
James Bedford 21                                                                                                                                      
Tony Bedford 50                                                                                                                                       
Fred Bloggs 22                                                                                                                                        
Pete Brown 37                                                                                                                                         
Mark Brown 46                                                                                                                                         
Francis Chepstow 56                                                                                                                                   
Stefan Heinz 52                                                                                                                                       
John Strange 51                                                                                                                                       
Simon Strange 62                                                                                                                                      
Ana Villamor 44                                                                                                                                       
Alice Villamor 50

Ich habe versucht, nach Nachnamen und dann nach Vornamen zu sortieren, aber Sie werden sehen, dass die Villamors nicht in der richtigen Reihenfolge sind. Ich hatte gehofft, nach dem Nachnamen zu sortieren und dann, wenn die Nachnamen übereinstimmten, nach dem Vornamen zu sortieren.

Es scheint, dass es etwas gibt, wie das funktionieren soll, was ich nicht verstehe. Ich könnte das natürlich auch anders machen (mit awk), aber ich möchte sort verstehen.

Ich verwende die Standard-Bash-Shell unter Mac OS X.

Harry
quelle

Antworten:

159

Eine Schlüsselangabe wie -k2bedeutet, dass alle Felder von 2 bis zum Ende der Zeile berücksichtigt werden. So Villamor 44landet vor Villamor 50. Da diese beiden nicht gleich sind, reicht der erste Vergleich sort -k2 -k1aus, um diese beiden Zeilen zu unterscheiden, und der zweite Sortierschlüssel -k1wird nicht aufgerufen. Wenn die beiden Villamors das gleiche Alter gehabt hätten, -k1hätten sie veranlasst, nach Vornamen sortiert zu werden.

Verwenden Sie -k2,2zum Sortieren nach einer einzelnen Spalte die Schlüsselspezifikation. Dies bedeutet, dass die Felder von # 2 bis # 2 verwendet werden, dh nur das zweite Feld.

sort -k2 -k3 <people.txtist überflüssig: es ist äquivalent zu sort -k2 <people.txt. Führen Sie den folgenden Befehl aus, um nach Nachnamen, Vornamen und Alter zu sortieren:

sort -k2,2 -k1,1 <people.txt

oder gleichwertig, sort -k2,2 -k1 <people.txtda es nur diese drei Felder gibt und die Trennzeichen gleich sind. In der Tat erhalten Sie den gleichen Effekt sort -k2,2 <people.txt, da sortdie gesamte Zeile als letzte Möglichkeit verwendet wird, wenn alle Schlüssel in einer Teilmenge von Zeilen identisch sind.

Beachten Sie auch, dass das Standardfeldtrennzeichen der Übergang zwischen einem nicht leeren und einem leeren Feld ist, sodass die Schlüssel die führenden Leerzeichen enthalten (in Ihrem Beispiel ist in der ersten Zeile der erste Schlüssel "Emily", aber der zweite Schlüssel " Bedford". Fügen Sie den hinzu -bOption zum Entfernen dieser Rohlinge:

sort -b -k2,2 -k1,1

Dies kann auch auf Schlüsselbasis erfolgen, indem das bFlag am Ende der Schlüsselstartspezifikation hinzugefügt wird :

sort -k2b,2 -k1,1 <people.txt

Aber etwas im Auge zu behalten: Sobald Sie eine solche Fahne auf die Schlüsselspezifikation hinzufügen, die globalen Flags (wie -n, -r...) nicht mehr auf sie anzuwenden sind, so ist es besser, pro-key - Flaggen und globale Flags zu vermeiden mischen.

Gilles
quelle
6
Du hast den Nagel auf den Kopf getroffen. Ich war davon ausgegangen (was gefährlich ist), dass die Angabe von -k1 die Verwendung von Feld 1 bedeutet, wobei das Feld am Standardfeldtrennzeichen (Leerzeichen) endet. Wie Sie jedoch deutlich machen, werden Sie bei der Option k aufgefordert, die Start- und Stoppunkte des Schlüssels anzugeben, bei denen es sich möglicherweise um ein einzelnes Feld handelt. Ihre Lösung funktioniert einwandfrei, und was noch wichtiger ist, mir ist klar, warum dies so ist. Danke vielmals.
Harry
Das ist riesig. So viele andere Quellen über KEYDEF sprechen von -k1 -k2, ohne die Bedeutung der COMMA im Format hervorzuheben, um zu begrenzen, welche Spalten in jedem Sortierschritt berücksichtigt werden. Ich war stundenlang damit beschäftigt, bis ich diese Antwort fand. Und die Manpage ist hier verwirrend. Es erklärt nicht, dass die Positionen "Start" und "Stopp" mit der Komma-Notation angegeben sind. Danke!
Jason Rohrer
16

Mit GNU sortmachst du das so, nicht sicher über MacOS:

sort -k2,2 -k1 <people.txt

Update laut Kommentar. Zitat von man sort:

   -k, --key=KEYDEF
          sort via a key; KEYDEF gives location and type

   KEYDEF is F[.C][OPTS][,F[.C][OPTS]] for start and stop position, where
   F is a field number and C a character position in the field; both are
   origin 1, and the stop position defaults to the line's end.
Mann bei der Arbeit
quelle
4
Könnten Sie bitte diese merkwürdige Schreibweise erklären?
Scai
1
Das hat mich zum richtigen Denken gebracht - danke dafür. Aber müssen Sie nicht den Stoppunkt für das zweite -k angeben? Das heißt -k2,2 -k1,1 sonst wird der Haltepunkt als Linienende genommen?
Harry
@ TonyBedford, richtig. Wenn Sie jedoch die Stoppposition nicht angeben, wird das Ergebnis für Ihre aktuelle Eingabe nicht geändert, sondern die Konsistenz erzwungen, falls Sie jemals mehrere Zeilen mit identischen Feldern 2 und 1 haben sollten. Ich bevorzuge es, dass die letzte Zeile -kso viel wie möglich einschließt.
Manatwork
1
@manatwork Das sollte nicht nötig sein; Wenn alle angegebenen Felder gleich sind, sortwird die gesamte Zeile verglichen. Oder mit GNU können sortSie -sfür eine stabile Sortierung verwenden.
2.