Наши партнеры

UnixForum






Книги по Linux (с отзывами читателей)

Библиотека сайта rus-linux.net

На главную -> MyLDP -> Тематический каталог -> Работа в консоли Linux

HuMan: sort

Автор: Алексей Дмитриев
Дата: 25 декабря 2008

Вступление

Команда sort сортирует содержимое файла в алфавитном или нумерологическом порядке. Если задать несколько файлов, то команда sort соединит их и, рассортировав, выдаст единым выводом. По умолчанию, объектом сортировки будут строки, однако опции позволяют выбирать объект сортировки: колонки, столбцы и прочие элементы форматирования файла. Разделителем между ними служат пробелы, однако соответствующие опции позволяют задать иные разделители.

Команда sort весьма древняя, она может служить образцом программирования утилит в ранних 70-х годах прошлого века. У команды множество опций, и их разнообразные сочетания, а также способы задания разделителей, хорошо развивают память и воображение.

Программа sort без опций

Я составил список своих должников и записал их в файл debts.txt:
Vova: 100$ -- September 3  2008
Sergey: 10$ -- December 30 2008
Misha:  25$ -- May 12 2008
Taras:  500$ -- June 24 2008

Если мне придет в голову рассортировать должников по алфавиту, то я дам команду:

$ sort debts.txt

Misha:  25$ -- May 12 2008
Sergey: 10$ -- December 30 2008
Taras:  500$ -- June 24 2008
Vova: 100$ -- September 3  2008

А могу и в обратном алфавиту порядке:

$ sort -r debts.txt

Vova: 100$ -- September 3  2008
Taras:  500$ -- June 24 2008
Sergey: 10$ -- December 30 2008
Misha:  25$ -- May 12 2008

Опции -n и -k

(--numeric-sort --key)

Приходится рассматривать эти две опции вместе, так как они позволяют сразу ввести читателя в курс дела, а не рассматривать множество простых примеров.

Опция -n используется всегда, когда нужно сортировать числа, разумеется в порядке возрастания (или убывания, добавив опцию -r).

Опция -k позволяет задавать объект сортировки: все эти столбцы, колонки, и тому подобные элементы форматирования файла.

Итак, я хочу выявить самых злостных должников по мере убывания долга:

$ sort -nrk 2 debts.txt

Taras:  500$ -- June 24 2008
Vova: 100$ -- September 3  2008
Misha:  25$ -- May 12 2008
Sergey: 10$ -- December 30 2008

Опция -n сообщает команде, что сортировать придется числа, опция -r , что в обратном порядке, а опция -k задает объект - вторую колонку текста.

У нас есть еще одна колонка с числами месяцев, можно для тренировки рассортировать список по числам разных месяцев, хотя никакого практического смысла это не имеет:

$ sort -nk 5 debts.txt

Vova: 100$ -- September 3  2008
Misha:  25$ -- May 12 2008
Taras:  500$ -- June 24 2008
Sergey: 10$ -- December 30 2008

Видите, в пятой колонке числа идут по возрастающей. Древность программы сказывается еще и в том, что можно вводить опции по-разному. Например можно набрать:

$ sort -nk5 debts.txt

Vova: 100$ -- September 3  2008
Misha:  25$ -- May 12 2008
Taras:  500$ -- June 24 2008
Sergey: 10$ -- December 30 2008

с тем же результатом. И даже:

$ sort -k5n debts.txt

Vova: 100$ -- September 3  2008
Misha:  25$ -- May 12 2008
Taras:  500$ -- June 24 2008
Sergey: 10$ -- December 30 2008

Должен предупредить, что существует два стиля задания объекта сортировки. (Имейте в виду, по-английски эти объекты называются fields, что обычно переводится как поля. Если видите в манах слова: fields или поля, знайте - речь идет о колонках, столбцах, или иных элементах форматирования текста). Но вернемся к стилям задания этих самых "полей".

Новый стиль использует опцию -k и цифры, указывающие порядковый номер нужной колонки с начала строки (начиная с 1).Возьмем файл pay.txt:

1. I.A.Ivanov  1700
2. J.P.Zaytsev 1955
3. T.N.Petrova 1000
4. A.D.Afonin  1300

И дадим команду:

$ sort -k2 pay.txt

4. A.D.Afonin  1300
1. I.A.Ivanov  1700
2. J.P.Zaytsev 1955
3. T.N.Petrova 1000

Сортировка произошла по первому символу второй колонки, что не дало нам никакой пользы. Изменим команду:

$ sort -k2.5 pay.txt

4. A.D.Afonin  1300
1. I.A.Ivanov  1700
3. T.N.Petrova 1000
2. J.P.Zaytsev 1955

Указав пятый символ (считая точки) во второй колонке (-k2.5), мы получили алфавитный список сотрудников.

Теперь рассмотрим файл ivanov.txt:

1. Filin F.F.   200$
2. Ivanov R.P.  120$
3. Alekseev I.O.  110$
4. Ivanov N.S.  300$
5. Klenov G.A.  233$
6. Ivanov I.A.  178$
7. Zaitsev B.I. 467$

Рассортируем его строго по второй колонке, не принимая во внимание колонку с инициалами:

$ sort -k 2,2 ivanov.txt

3. Alekseev I.O.  110$
1. Filin F.F.   200$
2. Ivanov R.P.  120$
4. Ivanov N.S.  300$
6. Ivanov I.A.  178$
5. Klenov G.A.  233$
7. Zaitsev B.I. 467$

Для этого мы применили опцию -k 2,2. Первая двойка означает начало объекта сортировки (колонки текста), а вторая двойка через запятую - конец объекта сортировки. То есть команде запрещено использовать для сортировки символы после последней буквы второй колонки.

Мы видим, что Ивановы идут в том же порядке, что и в исходном файле. А если мы хотим рассортировать Ивановых в алфавитном порядке их инициалов?

$ sort -k 2,3 ivanov.txt

3. Alekseev I.O.  110$
1. Filin F.F.   200$
6. Ivanov I.A.  178$
4. Ivanov N.S.  300$
2. Ivanov R.P.  120$
5. Klenov G.A.  233$
7. Zaitsev B.I. 467$

Мы приказали использовать для сортировки вторую и третью колонки текста (-k 2,3). Теперь Ивановы отсортированы и по инициалам. Но важнее знать, кто из Ивановых больше должен:

$ sort -k 2,2 -k 4n ivanov.txt

3. Alekseev I.O.  110$
1. Filin F.F.   200$
2. Ivanov R.P.  120$
6. Ivanov I.A.  178$
4. Ivanov N.S.  300$
5. Klenov G.A.  233$
7. Zaitsev B.I. 467$

Теперь инициалы не сортируются, так как первичная сортировка проводится строго по второй колонке (-k 2,2), а вторичная сортировка (-k 4n) по 4 колонке, в нумерологическом порядке, и только среди Ивановых (то есть тех, кто не различался по результатам первичной сортировки). Теперь становится понятно, зачем нужна такая сортировка - строго по заданному объекту.

Надеюсь, что новый стиль написания опции -k стал понятен. Новый стиль применяется на всех современных версиях команды sort, включая GNU Coreutils, которыми укомплектованы все новые дистрибутивы Линукс.

Вкратце коснусь старого стиля написания опции -k. Вот пример задания третьего столбца для нумерологической сортировки:

Новый стиль: sort -k 3,3n имя_файла

Старый стиль: sort +2 -3n имя_файла

Старый стиль работает на новых версиях программы, но рассматривать его в подробностях я не стану, чтобы не запутаться самому и не запутать читателя. Нам хватит путаницы и с новым стилем.

Опция -r

(--reverse)

Мы уже познакомились с ней. Она заставляет команду sort сортировать в обратном порядке. (От 'z' к 'a' и от 1000000 к 0).

Опция -M

(--month-sort)

Я не могу не остановится на одной удивительной способности команды sort - она может сортировать даже месяцы. Вернемся к файлу debts.txt:

Vova: 100$ -- September 3  2008
Sergey: 10$ -- December 30 2008
Misha:  25$ -- May 12 2008
Taras:  500$ -- June 24 2008

Названия месяцев у нас в 4 колонке, поэтому пишем:

$ sort -k 4M debts.txt

Misha:  25$ -- May 12 2008
Taras:  500$ -- June 24 2008
Vova: 100$ -- September 3  2008
Sergey: 10$ -- December 30 2008

Вуаля! Мне это почему-то кажется чудом. Можно задать ту же команду и по-другому:

$ sort -Mk 4 debts.txt

Misha:  25$ -- May 12 2008
Taras:  500$ -- June 24 2008
Vova: 100$ -- September 3  2008
Sergey: 10$ -- December 30 2008

Опция М преобразует первые три непробельные символа указанного столбца в заглавные буквы (Скажем, SEP означает SEPtember), а затем сравнивает их и располагает в порядке годового круга.

До сих пор мы рассматривали файлы, в которых разделителем колонок или столбцов был пробел, что и установлено по умолчанию. Чтобы задать другой разделитель, необходимо использовать опцию -t.

Опция -t

(--field-separator=РАЗДЕЛИТЕЛЬ)

Позволяет указать иной разделитель объектов сортировки вместо пробела. Вернемся к файлу pay.txt:

1. I.A.Ivanov  1700
2. J.P.Zaytsev 1955
3. T.N.Petrova 1000
4. A.D.Afonin  1300

В первый раз, чтобы рассортировать должников по фамилиям, мы указывали пятую букву в третьем столбце (считая разделителем столбцов пробелы). Есть другой способ решить эту задачу:

$ sort -t '.' -k4 pay.txt

4. A.D.Afonin  1300
1. I.A.Ivanov  1700
3. T.N.Petrova 1000
2. J.P.Zaytsev 1955

Теперь мы задали разделителем точку и указали четвертую колонку.

Давайте рассортируем по алфавиту шеллы, доступные в системе, указав разделителем слэш (-t '/'):

$ sort -t '/' -k2 /etc/shells

/bin/ash
/bin/bash
/bin/csh
/bin/ksh
/bin/tcsh
/bin/zsh 

В директории /etc масса файлов, в которых встречаются различные разделители. Часто это двоеточие:

$ sort -t ':' -k3n /etc/passwd

Эта команда рассортирует файл /etc/passwd в порядке возрастания идентификационных номеров пользователей. Проделайте этот пример самостоятельно, у него длинный вывод, боюсь мой редактор будет не в восторге.

Надеюсь, с разделителями все ясно, правила их задания очень напоминают команду cut.

Опция -c

(--check)

Эта опция проверяет порядок сортировки, сама ничего не сортируя. Создадим файл 123.txt:

3
1
4
2

Дадим команду:

$ sort -cn 123.txt
sort: 123.txt:2: неправильный порядок: 1

Вывод "неправильный порядок: 1" сообщает нам номер строки с первой ошибкой.

Опция -u

(--unique)

Скрывает одинаковые объекты. Если в процессе сортировки выявилось несколько одинаковых объектов, то будет выведен только первый из них, остальные проигнорированы:

$ sort -uk 2,2 ivanov.txt

3. Alekseev I.O.  110$
1. Filin F.F.   200$
2. Ivanov R.P.  120$
5. Klenov G.A.  233$
7. Zaitsev B.I. 467$

Остался только один Иванов из трех.

Если бы мы задали команду чуть по-другому:

$ sort -uk2 ivanov.txt

3. Alekseev I.O.  110$
1. Filin F.F.   200$
6. Ivanov I.A.  178$
4. Ivanov N.S.  300$
2. Ivanov R.P.  120$
5. Klenov G.A.  233$
7. Zaitsev B.I. 467$

то все Ивановы остались бы на своих местах. Ответьте: почему? (Ответ в приложении [1]).

Опция -b

(--ignore-leading-blanks)

Игнорирует пробелы в начале строк, и сортирует так, словно пробелов нет. Возьмем файл run.txt

dog
 cat
  horse

Применим команду sort без опций:

$ sort run.txt

  horse
 cat
dog

Фактически произошла сортировка по количеству пробелов, так как пробел предшествует буквам в порядке сортировки. Введем команду:

$ sort -b run.txt

 cat
dog
  horse

Теперь строки отсортированы в алфавитном порядке, невзирая на пробелы.

Немного усложним файл run.txt

horrible dog
black  cat
beautiful   horse

И попробуем выстроить животных по алфавиту:

$ sort -k2 run.txt

beautifull   horse
black  cat
horrible dog

Ничего не выходит - сортируется количество пробелов. Но стоит добавить опцию -b

$ sort -bk2 run.txt

black  cat
horrible dog
beautifull   horse

как все становится на свои места.

Опция -d

(--dictionary-order)

Признает только буквы, цифры и пробелы и сортирует как в словаре.

Опция -i

(--ignore-nonprinting)

Весьма похожа на предыдущую опцию -d, но не такая "строгая". Она признает только печатные символы, игнорируя все специальные.

Опция -f

(--ignore-case)

При обычной сортировке, заглавные буквы идут прежде строчных:

$ sort case.txt
 
Ivanov
Zaitsev
alfa
kurtka
romashka

А с опцией -f все равны:

$ sort -f case.txt

alfa
Ivanov
kurtka
romashka
Zaitsev

Опция -g

(--general-numeric-sort)

Позволяет сортировать числа, записанные в общей математической форме. Возьмем файл notation.txt:

11.11
 1
12.3
  567
10e1
10e0
9
345 
8.95
99

и попробуем рассортировать его обычной опцией -n:

$ sort -n notation.txt
 1
9
10e0
10e1
99
12.3
345
  567
8.95
11.11

и потерпим неудачу. Тогда применим опцию -g:

$ sort -g notation.txt
 1
8.95
9
10e0
11.11
12.3
99
10e1
345
  567

Теперь числа выстроились по ранжиру. Нужно сказать, что применять опцию -g следует в крайних случаях, когда нельзя обойтись опцией -n. Дело в том, что опция -g медленнее, и на больших файлах это становится заметным. Кроме того, в Интернете появляются сообщения, что в некоторых версиях GNU Coreutils sort замечены сбои в работе опции -g на больших файлах (Больше 25Мб).

Опция -T

(--temporary-directory=КАТАЛОГ)

Позволяет указать директорию для временных файлов, иную, чем положено по умолчанию (/tmp или $TMPDIR).

$ sort -T /имя_каталога filename

При сортировке больших файлов программа создает временные файлы, и можно указать, где их разместить. Примера по очевидным причинам дать не могу, просто помните про эту опцию, если случится сортировать многомегабайтные файлы.

Опция -S

(--buffer-size=РАЗМЕР)

Также пригодится для сортировки больших файлов. Она создаст в основной памяти буфер указанного размера.

Кстати, коли речь зашла о работе с большими файлами, то полезно бывает переместить эти процессы на задний план, чтобы можно было работать, не дожидаясь завершения процесса:

$ sort большой_файл &

Опция -s

(--stable)

Эта опция отменяет пересортировку. Что это такое? - Допустим, мы сортировали некий файл по определенным, нужным нам объектам сортировки (полям, столбцам, колонкам, символам внутри колонок и так далее), применяли вторичную сортировку (как в примере sort -k 2,2 -k 4n ivanov.txt), но все объекты, выбранные нами, оказались одинаковыми (равными). В этом случае, по умолчанию, команда sort проводит пересортировку, считая объектом сортировки всю строку в целом (как в случае сортировки без опций). Если мы хотим сохранить первоначальный порядок строк файла, и не проводить финальную пересортировку, то мы применяем опцию -s.

Опция -z

(--zero-terminated)

Эта опция рассматривает исходный файл как набор строк, разделенных не знаком переноса строки, а нулевым байтом. Для чего это может понадобиться, я не знаю. Единственное, что мне удалось найти, это туманные указания, что эта опция может оказаться полезной в составе программных каналов (pipes) с такими командами как 'perl -0', 'find -print0', и 'xargs -0' для сортировки произвольных файловых имен. Я пока не разбирался с перечисленными программами и не могу ничего сказать по этому поводу. Я также пробовал подставлять опцию -z в многочисленные примеры из этой статьи, но никакой сортировки после добавления этой опции не происходило.

Опция -o

(--output=ФАЙЛ)

Видимо пережиток прошлого, когда не было перенаправления вывода. С помощью этой опции можно задать файл, куда будет помещен вывод команды вместо стандартного вывода на экран дисплея.

Послесловие

Оставшиеся опции варьируют в различных мануалах и руководствах, они достаточно понятны интуитивно, и не требуют специального рассмотрения. Если какой-то из опций нет в вашем мане, не беда, скорее всего опция поддерживается вашей версией программы. Попробуйте, чем вы рискуете? Но попадаются опции, не поддерживаемые GNU Coreutils sort, как например опции -R и --random-source=file, позволяющие "рассортировать" строки и прочие объекты в случайном порядке.

Другое дело сочетания различных опций друг с другом. Если вы соорудили заковыристое заклинание из множества разных опций, а оно не работает, попробуйте убрать ту или другую опцию, может быть поможет. А лучше идти от простого к сложному, постепенно усложняя команду, пока не достигнете нужного результата. Хорошо помогает набрать вашу команду в поисковой строке Гугла, вдруг повезет. Кстати, только этим способом я смог разобраться с некоторыми опциями, о которых пишу в этой статье.

Команда sort и кириллица

Команда sort работает с нашими буквами неадекватно.

Резюме команды sort

Чрезвычайно полезная и "мощная" команда. С ее помощью не проблема выявить в огромной директории файлы, которые вы вчера потеряли (по дате модификации), или собрать "в кучу" все линки, чтобы их проверить, и так далее. А в качестве фильтра в программных каналах (pipes), эта команда просто незаменима и позволяет творить чудеса.

Приложение

[1] Ответ на задачу про Ивановых.

В первом случае мы задали сортировку строго по второй колонке (-uk 2,2), поэтому программа видела всех Ивановых одинаковыми, и включила опцию -u.

Во втором случае мы задали сортировку, не указав конец объекта сортировки (-uk2), и программа сортировала Ивановых по следующим колонкам (могла бы до конца строки). В этом случае одинаковых объектов выявлено не было, и опция -u не включилась.



Средняя оценка 5 при 1 голосовавших