Намагаємося сортувати два поля, друге, а потім перше


106

Я намагаюся сортувати по декількох стовпцях. Результати не такі, як очікувалося.

Ось мої дані (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

Наступне працює правильно:

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

Але наступне не працює, як очікувалося:

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

Я намагався сортувати за прізвищем, а потім за прізвищем, але ви побачите, що Villamors не в правильному порядку. Я сподівався сортувати за прізвищем, а потім, коли прізвища збігаються, сортувати за прізвищем.

Здається, є щось про те, як це має працювати, я не розумію. Я міг би зробити це іншим способом звичайно (використовуючи awk), але я хочу зрозуміти сортування.

Я використовую стандартну оболонку Bash на Mac OS X.

Відповіді:


159

Ключова специфікація, наприклад, -k2означає врахувати всі поля від 2 до кінця рядка. Так Villamor 44закінчується раніше Villamor 50. Оскільки ці два не є рівними, першого порівняння sort -k2 -k1достатньо для розрізнення цих двох рядків, а другий ключ сортування -k1не викликається. Якби два Villamors мали однаковий вік, -k1це призвело б до їх сортування за прізвищем.

Для сортування за одним стовпцем використовуйте -k2,2як специфікацію ключа. Це означає використовувати поля від №2 до №2, тобто лише друге поле.

sort -k2 -k3 <people.txtє зайвим: це рівнозначно sort -k2 <people.txt. Щоб сортувати за прізвищами, потім іменами, а потім за віком, виконайте таку команду:

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

або рівнозначно, sort -k2,2 -k1 <people.txtоскільки існують лише ці три поля і роздільники однакові. Насправді ви отримаєте такий самий ефект sort -k2,2 <people.txt, оскільки sortвикористовуєте весь рядок в крайньому випадку, коли всі ключі в підмножині рядків однакові.

Також зауважте, що роздільником полів за замовчуванням є перехід між незапущеним та порожнім, тому ключі включатимуть провідні пробіли (у вашому прикладі для першого рядка буде перший ключ "Emily", а другий ключ " Bedford". Додайте -bваріант зняти ці заготовки:

sort -b -k2,2 -k1,1

Це також можна зробити на основі ключа, додавши bпрапор наприкінці специфікації ключа запуску:

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

Але слід пам’ятати: як тільки ви додаєте один такий прапор до ключової специфікації, глобальні прапори (наприклад -n, -r...) більше не застосовуються до них, тому краще уникати змішування прапорів-ключів та глобальних прапорів.


6
Ти впорався. Я припускав (небезпечна річ), що вказівка ​​-k1 буде означати використання поля 1, де поле закінчується на роздільнику полів за замовчуванням (пробіл). Але як ви чітко зазначаєте, опція k очікує, що ви вкажете точки початку та зупинки ключа, які можуть бути, а можуть бути не одним полем. Ваше рішення працює чудово, і що ще важливіше, я зрозумів, чому це робиться. Дуже дякую.
Гаррі

Це ВЕЛИЧЕЗНО. Так багато інших джерел про KEYDEF говорять про -k1 -k2, не підкреслюючи важливості COMMA у форматі, щоб обмежити, які стовпці розглядаються на кожному етапі сортування. Я був затриманий на цьому годинами, поки не знайшов цієї відповіді. І сторінка man тут заплутана. Це не пояснює, що місця "пуск і зупинка" вказані комою. Дякую!
Джейсон Рохрер

16

З GNU sortви робите це так, не впевнені в MacOS:

sort -k2,2 -k1 <people.txt

Оновлення відповідно до коментаря. Цитується з 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.

4
Не могли б ви пояснити це дивне позначення?
scai

1
Це змусило мене думати по правильній лінії - дякую за це. Але чи не потрібно вказувати точку зупинки для другого -k. Тобто -k2,2 -k1,1, інакше точка зупинки приймається за кінець рядка?
Гаррі

@TonyBedford, правильно. Але не вказавши положення зупинки, це не змінить результат для вашого поточного введення, але змусить послідовність у випадку, якщо у вас коли-небудь буде кілька рядків з однаковими полями 2 та 1. Тому я вважаю за краще дозволити останнім -kвключити стільки, скільки може.
манатура

1
@manatwork Це не повинно бути необхідним; якщо всі вказані поля порівняти рівними, sortпорівняємо весь рядок. Або з GNU sortможна використовувати -sдля стабільного сортування.
аугурар
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.