Essayer de trier sur deux champs, en second puis en premier

106

J'essaie de trier sur plusieurs colonnes. Les résultats ne sont pas comme prévu.

Voici mes données (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

Ce qui suit fonctionne correctement:

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

Mais ce qui suit ne fonctionne pas comme prévu:

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

J'essayais de trier par nom de famille, puis par prénom, mais vous verrez que les Villamors ne sont pas dans le bon ordre. J'espérais trier par nom de famille, puis lorsque les noms de famille correspondaient, trier par prénom.

Il semble qu'il y ait quelque chose dans la façon dont cela devrait fonctionner que je ne comprends pas. Je pourrais faire cela d'une autre manière bien sûr (en utilisant awk), mais je veux comprendre le genre.

J'utilise le shell Bash standard sous Mac OS X.

Harry
la source

Réponses:

159

Une spécification de clé telle que -k2signifie prendre en compte tous les champs de 2 à la fin de la ligne. Donc Villamor 44finit avant Villamor 50. Comme ces deux valeurs ne sont pas égales, la première comparaison entre sort -k2 -k1suffit à discriminer ces deux lignes et la seconde clé de tri -k1n'est pas invoquée. Si les deux Villamors avaient eu le même âge, -k1ils auraient été triés par prénom.

Pour trier sur une seule colonne, utilisez-la -k2,2comme spécification de clé. Cela signifie utiliser les champs de # 2 à # 2, c'est-à-dire uniquement le deuxième champ.

sort -k2 -k3 <people.txtest redondant: cela équivaut à sort -k2 <people.txt. Pour trier les noms, les prénoms, puis l'âge, exécutez la commande suivante:

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

ou équivalent sort -k2,2 -k1 <people.txtpuisqu'il n'y a que ces trois champs et que les séparateurs sont les mêmes. En fait, vous obtiendrez le même effet sort -k2,2 <people.txt, car sortutilise la ligne entière en dernier recours lorsque toutes les clés d’un sous-ensemble de lignes sont identiques.

Notez également que le séparateur de champ par défaut est la transition entre un non-blanc et un blanc. Par conséquent, les clés incluent les espaces en début de ligne (dans votre exemple, pour la première ligne, la première clé sera "Emily", mais la deuxième clé " Bedford". Ajoutez le -bpossibilité de dépouiller ces blancs:

sort -b -k2,2 -k1,1

Cela peut également être effectué clé par clé en ajoutant le bdrapeau à la fin de la spécification de début de clé:

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

Mais gardez à l'esprit que, dès que vous ajoutez un tel indicateur à la spécification de clé, les indicateurs globaux (tels que -n, -r...) ne leur sont plus applicables, il est donc préférable d'éviter de mélanger des indicateurs par clé et des indicateurs globaux.

Gilles
la source
6
Vous avez réussi. J'avais supposé (chose dangereuse) que spécifier -k1 signifierait utiliser le champ 1, où le champ se termine par le séparateur de champ par défaut (espace). Mais comme vous l'avez clairement indiqué, l'option k vous demande de spécifier les points de début et de fin de la clé, qui peuvent être ou non un seul champ. Votre solution fonctionne parfaitement et, plus important encore, je comprends très bien pourquoi. Merci beaucoup.
Harry
C'est énorme. Tant d'autres sources sur KEYDEF parlent de -k1 -k2 sans souligner l'importance du format COMMA dans le format pour limiter les colonnes prises en compte à chaque étape de tri. J'ai été bloqué pendant des heures jusqu'à ce que je trouve cette réponse. Et la page de manuel est déroutante ici. Cela n'explique pas que les emplacements "démarrer et arrêter" sont spécifiés avec la notation par virgule. Je vous remercie!
Jason Rohrer
16

Avec GNU, sortvous le faites comme ceci, vous n’êtes pas sûr de MacOS:

sort -k2,2 -k1 <people.txt

Mise à jour en fonction des commentaires. Cité de 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.
homme au travail
la source
4
Pourriez-vous s'il vous plaît expliquer cette notation étrange?
scai
1
Cela m'a amené à réfléchir dans la bonne direction - merci pour cela. Mais n'avez-vous pas besoin de spécifier le point d'arrêt pour le second -k. C'est -k2,2 -k1,1 sinon le point d'arrêt est pris comme fin de ligne?
Harry
@ TonyBedford, correct. Mais ne pas spécifier la position d’arrêt ne changera pas le résultat pour votre entrée actuelle, mais forcera la cohérence au cas où vous auriez plusieurs lignes avec les champs 2 et 1 identiques. Je préfère donc permettre à la dernière -kd’inclure autant que possible.
Manatwork
1
@manatwork Cela ne devrait pas être nécessaire; si tous les champs spécifiés sont identiques, sortcomparera toute la ligne. Ou avec GNU, sortvous pouvez utiliser -spour un tri stable.
augurar