Как стать автором
Обновить

Почему команда sort включает в себя функцию uniq? (Куда делась философия Unix!?)

Уровень сложностиСредний
Время на прочтение8 мин
Количество просмотров4.5K

Введение

Команда sort включает опцию -u для удаления повторяющихся строк.

$ printf '%s\n' 1 2 3 3 2 1 | sort -u
1
2
3

С точки зрения философии Unix, команды для сортировки строк (sort) и удаления дубликатов (uniq) должны быть отдельными. Однако команда sort имеет опцию -u, соответствующую функции uniq. В этой статье я хочу объяснить, почему это так, ссылаясь на книгу «Software Tools».

Кто добавил функцию uniq в команду sort?

Некоторые ярые сторонники философии Unix могут подумать, что GNU добавила эту опцию для удобства, не понимая философию Unix. Однако функция uniq была включена в Version 7 Unix, то есть её добавили сами разработчики Unix. Это ясно из документации к Version 7 Unix 1979 года.

sort: https://man.cat-v.org/unix_7th/1/sort
u Suppress all but one in each set of equal lines.
Ignored bytes and bytes outside keys do not participate
in this comparison.

Может показаться, что команды uniq на тот момент ещё не существовало, но команда sort была добавлена в Version 2 Unix в 1972 году, а команда uniq — в Version 3 Unix в 1973 году. Таким образом, когда в Version 7 Unix была добавлена опция -u, команда uniq уже существовала.

sort: 1972年 Version 2 Unix http://squoze.net/UNIX/v2man/man1/sort
uniq: 1973年 Version 3 Unix http://squoze.net/UNIX/v3man/man1/uniq

Кстати, опция -u была добавлена в команду sort начиная с Version 7 Unix, но в Version 6 Unix 1975 года уже существовала команда usort, которая объединяла функции сортировки и удаления дубликатов.

usort: http://squoze.net/UNIX/v6man/man1/sort

Следовательно, уже в 1975 году было признано целесообразным включить функцию uniq в команду sort. Опция -u является оригинальной функцией, и поэтому она присутствовала в ранних версиях BSD Unix. Возможно, её не было в самой первой версии BSD Unix, которая была основана на Version 6 Unix.

2.9BSD sort: https://man.freebsd.org/cgi/man.cgi?query=sort&manpath=2.9.1+BSD

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

Почему функция uniq включена в команду sort из-за эффективности

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

Если бы мы сначала использовали sort для сортировки строк, а затем передали бы результат на вход команде uniq для удаления дубликатов, это привело бы к дополнительной обработке данных. Напротив, использование команды sort с опцией -u позволяет избежать этих лишних шагов, делая процесс более прямолинейным и эффективным.

На современных высокоскоростных многоядерных процессорах и SSD, эта разница в эффективности может быть менее заметной. Многоядерные процессоры способны распределять нагрузку по нескольким ядрам, что уменьшает влияние разницы между использованием sort + uniq и sort -u. С теоретической точки зрения, использование sort + uniq может увеличить общее использование CPU, но фактическое время выполнения может быть сокращено за счет параллелизации процессов.

Прочтите «Software Tools»!

Шутка. На самом деле, это необязательно. Перевод на японский устарел, и аналогичную информацию можно найти в других книгах. Однако для своего времени эта книга была новаторской. «Software Tools» была опубликована в 1976 году, а японский перевод под названием «ソフトウェア作法» вышел в 1981 году. Эта книга описывает методы написания хороших программ, которые служат полезными инструментами. Её авторы — известные разработчики Unix, Брайан Керниган и Плаугер. Примечательно, что книга была опубликована между версиями Unix Version 6 (1975) и Version 7 (1979), то есть после появления команды usort, но до добавления опции -u в команду sort.

Эта книга объясняет методы программирования для создания хороших инструментов. Примечательно, что слово «作法» читается как «сакухо», а не «сахо» (как указано в предисловии переводчика). Таким образом, название перевода можно интерпретировать как «Методы создания программного обеспечения». В книге речь идет преимущественно о командах Unix, но не о написании сценариев оболочки для объединения инструментов. Книга была написана до появления Bourne Shell. В ней использовался язык Ratfor — улучшенная версия Fortran, предназначенная для структурного программирования (транспилер для Fortran). Примеры кода в книге нельзя реализовать в виде сценариев оболочки; это скорее руководство по созданию инструментов, которые вызываются из сценариев оболочки. Книга объясняет, какими должны быть программы, чтобы их было легко сопровождать и изменять, и основывается на принципах структурного программирования. Эти принципы стали стандартом к моменту публикации книги, но в 1960-х и начале 1970-х годов они только набирали популярность.

Фраза «Прочтите Software Tools!» не означает, что те, кто добавил опцию -u в команду sort, должны изучить философию Unix (автор книги сам был одним из разработчиков Unix). Речь идет о том, что разработчики Unix, понимая философию Unix, интегрировали функцию uniq в команду sort для повышения эффективности.

О «Правильном Разделении Функций»

Этот вопрос рассматривается на странице 196 книги «Software Tools», в главе 4 «Сортировка», раздел «4.7 Разделение функций — unique».

Часто встречается задача собрать одинаковые элементы в одном месте и обрабатывать их как группу. Иногда необходимо выделить только один элемент из этой группы и отбросить остальные.
(Пропуск)
Добавление такой функции в команду
sort несложно. (Но где в программе следует выполнять эту обработку?) Однако, действительно ли это должно быть частью команды sort?
Этот вопрос поднимает одну из основных проблем разработки хороших инструментов. Эта проблема заключается в правильном разделении функций.

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

«Один инструмент — одна функция»

Сколько функций должно быть встроено в одну программу? В данном случае, объединение функций удаления дубликатов с сортировкой повышает эффективность. Это экономит один проход по данным. Более того, сравнение двух строк на идентичность определяется используемой функцией сравнения. Объединение функций сортировки и удаления дубликатов гарантирует согласованность и повышает эффективность выполнения.

Автор книги также утверждает, что причиной добавления опции -u в команду sort является именно эффективность.

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

Как указано, команда uniq выполняет не только удаление дубликатов. Она обладает множеством функций.

$ printf '%s\n' foo foo bar baz baz | uniq -u
bar

$ printf '%s\n' foo foo bar baz baz | uniq -d
foo
baz

$ printf '%s\n' foo foo foo bar baz baz | uniq -c
3 foo
1 bar
2 baz

Опции -u и -d команды uniq существуют с Version 3 Unix, а опция -c появилась в Version 4 Unix.

uniq -u, -d: 1973年 Version 3 Unix http://squoze.net/UNIX/v3man/man1/uniq
uniq -c: 1973年 Version 4 Unix http://squoze.net/UNIX/v4man/man1/uniq

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

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

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

«Мы долгое время разделяли функции сортировки и удаления дубликатов»

Мы долгое время выполняли сортировку и удаление дубликатов с помощью отдельных программ. Но в конце концов проблема эффективности стала настолько важной, что эти программы были объединены. В команду sort была добавлена дополнительная функция для удаления соседних дубликатов. (Оригинальная программа для удаления дубликатов осталась и продолжала активно использоваться.) Однако вначале никто не думал, что это удачное сочетание. Какой урок можно извлечь из этого? Разделяйте функции, пока не поймете, как их правильно объединить.

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

Урок из уроков: «Если есть веская причина, объединяйте функции!»

Обратите внимание на мой главный вывод.

Я хочу сказать, что некоторые люди неправильно понимают философию Unix и считают, что разделение функций и создание однофункциональных команд является единственным правильным методом разработки в стиле Unix. Не нужно доводить все до крайностей. Мир не так прост.

Если взглянуть на реальные команды Unix, становится очевидно, что многие из них выполняют не только одну функцию. Существует множество команд, которые содержат несколько функций, которые можно было бы разделить. Это касается и ранних команд, разработанных на начальных этапах развития Unix. Хотя изначально команда могла выполнять только одну функцию, со временем в неё добавлялись дополнительные функции.

Идея о том, что одна команда должна выполнять только одну функцию, безусловно, является идеалом. Но в реальности это не всегда лучший выбор. Философия Unix говорит о том, что нужно стремиться к простоте. Но она не утверждает, что команда должна выполнять только одну функцию (внимательно прочитайте). Многие команды, выполняющие «одно дело хорошо», на самом деле имеют несколько функций. Например, команда uniq, которая, как предполагалось, должна выполнять только удаление дубликатов, на самом деле имеет несколько функций.

Иногда объединение нескольких функций в одну команду может быть нецелесообразным, но в других случаях это необходимо для достижения нужного результата. Например, как бы вы отфильтровали результаты команды ps, оставив заголовочную строку? Это легко сделать с помощью sed или awk, которые включают в себя функции grep.

$ ps | sed -n '1{p;n;}; /bash/p'
PID TTY TIME CMD
2885036 pts/25 00:00:00 bash

$ ps | awk 'NR==1 || /bash/'
PID TTY TIME CMD
2885036 pts/25 00:00:00 bash

Команда sed имеет множество функций, но она выполняет «одно дело» как потоковый текстовый редактор. Команда awk также имеет множество функций, но она выполняет «одно дело», а именно сопоставление шаблонов, обработку строк и чисел. Философия Unix не говорит о создании однофункциональных команд, а о том, чтобы каждая команда имела подходящие функции.

Идея в том, чтобы упростить выполнение задач, создав однофункциональные или малофункциональные команды, является хорошей. Однако это не означает, что создание команд с множеством функций противоречит философии Unix. Объединение нескольких функций в одну команду усложняет её, но если вы владеете навыками программирования для управления этой сложностью, проблем не возникнет. Это означает, что внутри одной программы функции должны быть реализованы как набор небольших модулей. Это концепция модульности, общая для философии Unix и структурного программирования. Важно правильно разделять функции: оставлять их раздельными до тех пор, пока не станет ясно, как их лучше объединить, и затем объединять их при наличии веских причин.

В заключение

Философия Unix может показаться сложной, но на самом деле она является основой для разработки хорошего программного обеспечения и естественно применяется современными программистами. Философия Unix — это не метод проектирования, а основное правило, позволяющее избегать излишней сложности. В реальности часто приходится идти на компромиссы по разным причинам.

Как указывается в книге «Software Tools», важно придерживаться принципов структурного программирования, но это не означает, что следование этим принципам автоматически приведет к созданию хороших программ. Главное — это писать код, который не усложняет задачи, делая системы легко переносимыми, улучшая читаемость и облегчая сопровождение. Это означает, что при программировании нужно думать о людях, которые будут использовать или поддерживать ваш код, а также о создании инструментов, которые будут полезны другим, и уметь пользоваться инструментами, созданными другими.

Бонус: Книги для изучения философии Unix

Проблема с вводящими в заблуждение названиями японских переводов (для личного использования):

  • The Elements of Programming Style (1974)

    • Автор: Брайан В. Керниган, П. Дж. Плаугер

  • Software Tools (1976)

    • Автор: Брайан В. Керниган, П. Дж. Плаугер

  • The Elements of Programming Style, 2nd Edition (1981)

    • Автор: Брайан В. Керниган, П. Дж. Плаугер

  • Software Tools in Pascal (1981)

    • Автор: Брайан В. Керниган, П. Дж. Плаугер

  • The Unix Programming Environment (1984)

    • Автор: Брайан В. Керниган, Роб Пайк

  • Program Design in the UNIX Environment (1984)

    • Автор: Брайан В. Керниган, Роб Пайк

  • The Practice of Programming (1999)

    • Автор: Брайан В. Керниган, Роб Пайк

Теги:
Хабы:
+2
Комментарии3

Публикации

Истории

Ближайшие события

Конференция «IT IS CONF 2024»
Дата20 июня
Время09:00 – 19:00
Место
Екатеринбург
Summer Merge
Дата28 – 30 июня
Время11:00
Место
Ульяновская область