Próbuję posortować według dwóch pól, najpierw następnie

106

Próbuję sortować według wielu kolumn. Wyniki nie są zgodne z oczekiwaniami.

Oto moje dane (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

Następujące działa poprawnie:

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

Ale następujące działania nie działają zgodnie z oczekiwaniami:

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

Próbowałem sortować według nazwiska, a następnie imienia, ale zobaczysz, że Villamors nie są w prawidłowej kolejności. Miałem nadzieję, że posortuję według nazwiska, a potem, gdy nazwiska będą pasować, posortuję według imienia.

Wydaje się, że jest coś w tym, jak to powinno działać, czego nie rozumiem. Mógłbym to zrobić oczywiście w inny sposób (używając awk), ale chcę to zrozumieć.

Używam standardowej powłoki Bash w Mac OS X.

Złupić
źródło

Odpowiedzi:

159

Kluczowa specyfikacja, np. -k2Oznacza uwzględnienie wszystkich pól od 2 do końca wiersza. Więc Villamor 44kończy się wcześniej Villamor 50. Ponieważ te dwa nie są sobie równe, pierwsze porównanie sort -k2 -k1wystarczy, aby rozróżnić te dwie linie, a drugi klawisz sortowania -k1nie jest wywoływany. Gdyby dwaj Villamors byli w tym samym wieku, -k1spowodowaliby, że zostali posortowani według imienia

Aby posortować według pojedynczej kolumny, użyj -k2,2jako specyfikacji klucza. Oznacza to użycie pól od # 2 do # 2, tj. Tylko drugie pole.

sort -k2 -k3 <people.txtjest zbędny: jest równoważny z sort -k2 <people.txt. Aby posortować według nazwisk, a następnie imion, a następnie wieku, uruchom następującą komendę:

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

lub równoważnie, sort -k2,2 -k1 <people.txtponieważ istnieją tylko te trzy pola, a separatory są takie same. W rzeczywistości uzyskasz ten sam efekt sort -k2,2 <people.txt, ponieważ sortużywa całej linii w ostateczności, gdy wszystkie klucze w podzbiorze linii są identyczne.

Zauważ też, że domyślnym separatorem pól jest przejście między niepustym a pustym, więc klucze będą zawierać wiodące spacje (w twoim przykładzie, dla pierwszego wiersza pierwszym kluczem będzie "Emily", ale drugim kluczem " Bedford". Dodaj -bopcja usunięcia tych pustych miejsc:

sort -b -k2,2 -k1,1

Można to również zrobić dla poszczególnych kluczy, dodając bflagę na końcu specyfikacji początkowej klucza:

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

Ale coś pamiętać: jak najszybciej dodać jedną taką flagę specyfikacji kluczowych, globalnych (jak flagi -n, -r...) nie stosuje się do nich tak, że lepiej jest unikać mieszania per kluczowych flagi i globalnych flag.

Gilles
źródło
6
Udało wam się. Założyłem (niebezpieczna rzecz do zrobienia), że określenie -k1 oznaczałoby użycie pola 1, gdzie pole kończy się domyślnym separatorem pól (spacją). Ale, jak wyraźnie zaznaczasz, opcja k oczekuje, że określisz punkty początkowe i końcowe klucza, które mogą, ale nie muszą być pojedynczym polem. Twoje rozwiązanie działa idealnie, a co ważniejsze, jestem jasne, dlaczego tak się dzieje. Wielkie dzięki.
Harry,
To jest ogromne. Tak wiele innych źródeł o KEYDEF mówi o -k1 -k2 bez podkreślania znaczenia COMMA w formacie, aby ograniczyć liczbę kolumn branych pod uwagę na każdym etapie sortowania. Utknąłem na tym przez wiele godzin, aż znalazłem tę odpowiedź. Strona man jest tutaj myląca. Nie wyjaśnia, że ​​lokalizacje „start i stop” są określone za pomocą notacji przecinkowej. Dziękuję Ci!
Jason Rohrer
16

Z GNU sortrobisz to w ten sposób, nie jestem pewien co do MacOS:

sort -k2,2 -k1 <people.txt

Zaktualizuj zgodnie z komentarzem. Cytat z 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.
człowiek w pracy
źródło
4
Czy mógłbyś wyjaśnić ten dziwny zapis?
scai
1
To skłoniło mnie do myślenia we właściwy sposób - dzięki za to. Ale nie musisz określać punktu zatrzymania dla drugiego -k. To jest -k2,2 -k1,1, w przeciwnym razie punkt zatrzymania jest traktowany jako koniec linii?
Harry,
@TonyBedford, poprawne. Ale nieokreślenie pozycji zatrzymania nie zmieni wyniku dla twojego aktualnego wejścia, ale wymusi spójność w przypadku, gdy kiedykolwiek będziesz miał wiele linii z identycznym polem 2 i 1. Więc wolę pozwolić, aby ostatnie -kzawierało jak najwięcej.
manatwork
1
@manatwork To nie powinno być konieczne; jeśli wszystkie podane pola sortbędą równe, porówna całą linię. Lub z GNU sortmożesz użyć -sdo stabilnego sortowania.
sierpień