Командная строка Unix полна сюрпризов. Например, вы знали, что инструмент ls
, который чаще всего используется для получения списка файлов в текущем каталоге, в версии OS X распознаёт не менее 38 разных флагов?
Я не знал, так что затвитил этот факт. И получил парочку ответов, один из которых заставил меня задуматься: действительно ли саму Unix нужно винить в этом?
Насколько я знаю, ни Linux, ни OS X не были спроектированы в строгом соответствии с философией Unix. Будет лицемерием основывать критику “Unix” только на этих производных от Unix, которые есть у нас сегодня.
Но я всё-таки попробую показать, как много проблем с интерфейсами командной строки в современных наследниках Unix восходят к корням самой Unix. В частности, я попытаюсь объяснить свой скепсис относительно идеи, что окружение командной строки Unix могло когда-либо поддерживать экосистему программ, каждая из которых хорошо выполняет одну функцию.
Но я немного опережаю события. Прежде чем я начну говорить об этом, давайте более пристально посмотрим на команду ls
и попробуем выяснить, что конкретно она делает не так.
Много всего, но слабо
Разные версии ls
распознают разные наборы флагов, но в целом эти флаги можно распределить по нескольким широким категориям. Пользователи ls
применяют флаги, чтобы:
- Определить формат выдачи.
−C
упорядочивает записи в аккуратную сетку;−m
выдаёт разделённый запятыми «поток» записей;−1
выдаёт каждую запись в своей собственной строке;−q
заменяет непечатаемые символы в названиях файлов вопросительными знаками. - Отобразить дополнительную информацию о каждом файле.
−F
добавляет символы к названиям каталогов (/
), исполняемым файлам (*
), симлинкам (@
) и другим «особым» типам файлов;−s
выдаёт размер каждого файла рядом с его именем. - Изменить порядок файлов.
−r
для списка в обратном порядке;−t
для списка файлов по времени последнего изменения. - Включить или исключить определённые файлы.
−a
включает в список файлы, скрытые по умолчанию;−R
показывает рекурсивный список файлов в подкаталогах текущего каталога.
Если опустить первую категорию флагов, то в остальных трёх есть кое-что интересное. Знатоки функционального программирования, в частности, могут увидеть в них нечто знакомое.
Так и есть — каждая из этих трёх категорий примерно соответствует единственной общей функции высшего порядка, которая работает с последовательностями!
- Флаги для отображения дополнительной информации о каждом файле можно выразить в терминах
map
, которая применяет заданную трансформацию на каждый элемент в последовательности независимо от других. - Флаги для изменения порядка файлов можно выразить в терминах
sort
, которая использует заданное условие для попарного сравнения элементов и соответствующей сортировки. - Флаги для включения и исключения определённых файлов можно выразить в терминах
filter
, которая проверяет каждый элемент относительно заданного утверждения и отклоняет те, что признаны неподходящими.
ls
кажется раздутой, потому что она действительно раздутая. Ряд функций высшего порядка могут взять на себя бóльшую часть функциональности, которая сейчас втиснута в ls
в виде флагов.
Что пошло не так?
Идея того, что каждая программа должна вмещать в себе самодостаточную единицу функциональности, ни в коем случае не нова. Десятилетиями сторонники Unix превозносили достоинства конвейеров: «программ», созданных на лету соединением маленьких компонуемых фильтров друг за другом. В таком случае как можно объяснить эволюцию настолько концептуально простого инструмента как ls
в сторону всё возрастающей сложности?
Экстремальная скупость командной строки Unix подсказывает одно возможное объяснение. Когда Unix изобрели, максимальная ширина экрана ограничивалась примерно 80 символами, а использование компьютера означало сидеть перед терминалом и набирать команды на клавиатуре. В таком окружении имело смысл пожертвовать удобочитаемостью и компонуемостью, чтобы втиснуть больше информации в минимально возможное количество символов.
В ту эпоху авторы самых популярных утилит сильно склонялись к тому, чтобы создавать сокращения везде, где только возможно. ls
называется ls
по той же самой причине, по которой её флаги представляют собой зашифрованные руны из одного символа вместо осмысленных слов или, не дай бог, целых фраз: она была разработана для небольшой группы узкоспециализированных экспертов в среде, где каждое нажатие клавиши, каждый символ на экране имел реальную и выразительную цену.
Аналогично и сами флаги являются сокращениями для наиболее общих сценариев реального использования. Зачем тратить время и добавлять шаг фильтрации к конвейеру, чтобы исключить скрытые файлы из выдачи, если 90% времени всё равно никто не хочет видеть скрытые файлы? Зачем отображать всю информацию о каждом файле, если 90% времени пользователю нужны только имена?
Такой образ ls
и окружения командной строки Unix в целом.
«Универсальный интерфейс»
Но почему не написать более простую альтернативу ls
— функцию, которая берёт произвольный каталог, или рабочий каталог по умолчанию, и возвращает список файлов из него, не обращая внимания на флаги? В конце концов, хакнуть Unix проще чем что-либо другое: если вам не нравится ls
, можете заменить её.
Отвечу на этот вопрос гипотезой. Представьте язык программирования, в котором каждая функция принимает в точности один аргумент (строку) и возвращает в точности один результат (другую строку).
Ой, посмотрите — такой язык существует, и он называется шелл.
Unix позволяет программам общаться друг с другом и с пользователем исключительно через потоки символов. Вы не можете написать функцию, которая возвращает список файлов, потому что шелл не знает, что такое «список», не знает, что такое «файлы» и не сможет сказать вам разницу между «функцией» и «программой», даже если от этого будет зависеть его жизнь.
Программы не «принимают аргументы» и не «возвращают значения», они читают символы из stdin
и печатают символы в stdout
!
Пишите программы для обработки текстовых потоков, потому что это универсальный интерфейс.
Дуглас Макилрой, «Проектирование программ в среде UNIX»
Изначальные архитекторы Unix рассматривали «простоту» текстовых потоков как преимущество. Поэтому они отказывались накладывать любую структуру на данные, которые передаются между программами. Это решение, призванное избежать ненужной сложности, вместо этого просто перенесло излишнюю сложность дальше вниз по течению.
Помните первую категорию флагов ls
— те флаги, которые мы не могли выдать за сокращения стандартных трансформаций последовательности? Выходит, что это просто сокращения для неформального кодирования условных списков файлов в виде строк, которые определённые другие программы (или, в некоторых случаях, человеческие существа) знают, как парсить.
Система неспособна предоставить абстракции, нужные пользователям. В ответ пользователи изобретают их заново, скудными, непоследовательными и не в тех местах. Это удручающе обычная практика.
Не приемлемый Unix
Знание истории информатики и ограничений, в которых формировались текущие ментальные модели, дарует нам своеобразную суперсилу: мы можем определить, когда необходимый прежде компромисс стал нелепым и устарел.
Многие проблемы юзабилити, которые поднял Дон Норман в своей критике Unix 1981 года, остались практически без изменений до настоящего времени. В качестве подарка мы разработали графические интерфейсы, которые держат «обычных пользователей» в стороне от командной строки, но по-прежнему предполагается, что «серьёзные разработчики» опустятся в явно негуманную среду, чтобы сделать там нечто осмысленное.
Вместо переоценки командной строки Unix с прицелом на улучшение юзабилити, когда современное оборудование уже не так ограничивает интерфейс, мы написали эмуляторы терминала, которые добросовестно воспроизводят ограничения середины 1970-х. Мы требуем от новых альтернативных оболочек совместимости с sh и слепо верим, что иерархические файловые системы — оптимальный способ организации информации.
Каковы шансы, что 40 лет назад мы как-то натолкнулись на лучший возможный интерфейс взаимодействия с компьютером? Другими словами, каковы шансы, что наши действия сегодня имеют смысл?
Даже самая ранняя версия Unix была только частной, имеющий недостатки реализацией философии Unix. Если мы хотим поощрить более широкое распространение философии, то не стóит защищать реализацию, преуменьшая её недостатки. Вместо этого нужно прямо взяться за эти недостатки. Строить системы, которые устраняют их, в то же время придерживаясь духа — если не буквы — принципов, на которых построен Unix.
Автор: m1rko