Пытается отсортировать по двум полям, потом второе потом первое


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не вызывается. Если бы у двух Вильяморов был одинаковый возраст, -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 в формате для ограничения количества столбцов, которые рассматриваются на каждом этапе сортировки. Я застрял на этом часами, пока не нашел ответ. И страница руководства здесь сбивает с толку. Это не объясняет, что места «запуска и остановки» указываются с помощью запятой. Спасибо!
Джейсон Рорер

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, иначе точка останова принимается за конец строки?
Гарри

@ ТониБедфорд, правильно. Но не указание позиции остановки не изменит результат для вашего текущего ввода, но приведет к согласованности в случае, если у вас когда-нибудь будет несколько строк с одинаковыми полями 2 и 1. Поэтому я предпочитаю, чтобы последняя -kвключала столько, сколько может.
Манатворк

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