Что такое unix way
Перейти к содержимому

Что такое unix way

  • автор:

Заметки программистера

Что такое «unix way»? Что кроется за этим словосочетанием? Почему идеология, рожденная десятилетия назад, до сих пор является актуальной и позволяет старикам юниксоидам козырять ей перед «оконным» молодняком? Эти вопросы беспокоили меня с первых дней моего знакомства с Linux. Пришла пора поделиться найденными ответами.

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

История UNIX

..все началось в 1969 году, когда в недрах компании AT&T. Силами ее подразделения Bell Labs была разработана первая UNIX система. Во главе сотрудников исследовательской группы Bell стояли Кен Томпсон (Ken Tompson) и Деннис Ритчи (Dennis Ritchie).

Интересны факт, первое название системы было UNICS (UNiplexed Information and Computing Service). Этот вариант был предложен сотрудником компании AT&T Брайаном Керниганом, и, уже несколько позднее, система обрела свое знаменитое и судьбоносное название UNIX.

Одним из важнейших результатов разработки UNIX можно смело назвать рождение бессмертного языка C. Дело в том, что первые версии системы писались на ассемблере, что, вместе с высокой производительностью, вносило некоторые ограничения в переносимость системы. Решение Томпсона переписать все на языке высоко уровня и привело к появлению нового языка, прародителем которого был язык BCPL. Теперь, для портирования системы на новую архитектуру, достаточно было написать для нее компилятор языка C.

Еще одним ключевым результатом разработки UNIX стали конвейеры. Конвейеры — это цепочка программ, каждая из которых выполняет некоторое действие и передает результат на вход следующей в конвейере программе. Таким образом, программы, выстраиваясь в цепочку, приводят к результату, который ограничен только воображением пользователя. Именно конвейеры стали ключевым изобретением в UNIX, породившим понятие Unix way.

Философия UNIX

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

  • Пишите программы, которые делают что-то одно и делают это хорошо.
  • Пишите программы, которые бы работали вместе.
  • Пишите программы, которые бы поддерживали текстовые потоки, поскольку это универсальный интерфейс».
  1. Красиво — небольшое.
  2. Пусть каждая программа делает что-то одно, но хорошо.
  3. Собирайте прототип как можно раньше.
  4. Предпочитайте переносимость эффективности.
  5. Храните данные в простых текстовых файлах.
  6. Используйте возможности программ для достижения цели.
  7. Используйте сценарии командной строки для улучшения функционала и переносимости.
  8. Избегайте пользовательских интерфейсов, ограничивающих возможности пользователя по взаимодействию с системой.
  9. Делайте каждую программу «фильтром».
  1. Позвольте пользователю настраивать окружение.
  2. Делайте ядра операционной системы маленькими и легковесными.
  3. Используйте нижний регистр и придерживайтесь кратких названий.
  4. Не храните тексты программ в виде распечаток («Спасите деревья!»).
  5. Не сообщайте пользователю об очевидном («Молчание — золото»).
  6. Разбивайте сложные задачи на несколько простых, выполняемых параллельно («Мыслите «параллельно»»).
  7. Объединённые части целого есть нечто большее, чем просто их сумма.
  8. Ищите 90-процентное решение.
  9. Если можно не добавлять новый функционал, не добавляйте его («Чем хуже, тем лучше»).
  10. Мыслите иерархически.

и снова спасибо вики за информацию

Потоки ввода/вывода и каналы.

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

Основой конвейеров программ в UNIX являются потоки ввода/вывода. Потоки ввода/вывода — это абстракции, позволяющие производить чтение и запись информации единым образом вне зависимости от того, что является ее источником (или приемником). Источником информации для потоков может быть файл, программа или устройство.

  • поток ввода(в него помещается информация вводимая с клавиатуры),
  • поток вывода(из него читается информация выводимая на экран)
  • поток ошибок(предназначенный для вывода отладочной информации и информации об ошибках)

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

команда > файл

Направляет стандартный поток вывода в файл. Если файл не существует, он будет создан; если существует — перезаписан сверху.

команда >> файл

Направляет стандартный поток вывода в файл. Аналогично предыдущей команде, создает файл в случае его отсутствия, но если файл существует, не затирает его, а дописывает новую информацию в конец файла.

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

$ iconv -f cp1251 -t utf8 < album_cp1251.cue >album_utf8.cue

Утилита iconv преобразует содержимое потока ввода из одной кодировки (указанной в параметре -f) в другую(указанную в параметре -t). Содержимое изначального файла выводится в поток ввода, а поток вывода перенаправляется в новый файл. Что делать после этого с файлом в старой кодировке решать Вам 🙂

Потоки можно перенаправлять не только в файл, но и в программы. Для этого существует команда | . Ее вызов эквивалентен следующей последовательности команд:

команда_1 > temp.txt команда_2 < temp.txt rm temp.txt

Это называется каналом(англ. pipe). Каналы позволяют с легкостью объединять программы в конвейеры и добиваться удивительных результатов.

Чтобы показать всю мощь и гибкость каналов, вернемся к "музыкальному" примеру. Как правило, вместе с .cue файлом идет большой файл .flac, содержащий в себе весь альбом описываемый .cue файлом. На сегодня далеко не все музыкальные плееры способны понимать, исходя из информации в .cue, что во .flac несколько треков. Это приносит серьезные неудобства. Как вариант решения проблемы, можно единый .flac разбить на кучу маленьких, содержащих ровно по одному треку, файлов. Для этого можно воспользоваться следующими утилитами:

  • cuebreakpoints - выводит время окончания треков из CUE или TOC файлов.
  • shnsplit - разбивает .flac файл на несколько маленьких в соответствии с временными отметками, поданными на вход программы.
  • cuetag - записывает теги на основе информации в .cue файле

$ cuebreakpoints "Pink Floyd - Animals.cue" | shnsplit -o flac "Pink Floyd - Animals.flac"

По умолчанию, имена файлов генерируются как split-track<номер>.flac. Чтобы изменить часть имени перед номером, существует ключ -n. Подробнее о нем в man странице.
Чтобы добавить в получившиеся файлы описание тегов, надо вызвать cuetag :

$ cuetag "Pink Floyd - Animals.cue" split-track*.flac

Главное, не забыть изменить кодировку .cue файла перед присваиванием тегов ;)

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

$ find /home/music -iname "*.mp3" -print0 | xargs -0 mid3iconv -e CP1251 --remove-v1

Разве не прелесть? А если вынести адрес директории в параметр, да повесить на такой конвейер псевдоним, то вообще красота!

И так, мы подошли к инструменту, который делает подход unix не просто интересным и местами удобным, а по настоящему мощным инструментом в решении повседневных задач. Я говорю о скриптах - последовательности команд, записанных в файл и выполняемых как одна единая команда.

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

#!/bin/sh if [ $# == 0 ]; then echo "Не указана директория" exit -1 fi find $1 -iname "*.mp3" -print0 | xargs -0 mid3iconv -e CP1251 --remove-v1 

Чтобы сделать файл исполняемым, выполните

Приведенный скрипт подставляет первый аргумент вызова в качестве директории в команду find. Обратите внимание на конструкции $# и $1. Первая - это переменная, возвращающая количество аргументов вызова скрипта, вторая переменная возвращает первый аргумент вызова. Скрипт проверяет количество аргументов и, если аргументы не были указаны, сообщает об этом и завершает свою работу. Скрипт далек от идеала и не решает многих нюансов с передачей директорий в качестве параметра, но аккуратно записанный полный путь вполне должен дать скрипту корректно отработать.

Итоги

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

Что еще почитать:
  1. История компьютерного андеграунда
  2. История OC UNIX
  3. Философия UNIX
  4. Стандартные потоки ввода/вывода
  5. Искусство программирования на языке сценариев командной оболочки

Разбираем по полочкам «философию Unix»

image

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

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

«Философия Unix»

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

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

Другие формулировки сильнее тяготеют к прямому руководству (также из Википедии):

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

Иногда эти советы могут быть рациональны, но не всегда. Сломано немало копий по поводу того, «нарушают» ли эту философию утилиты ядра GNU потому, что у них так много флагов и т. п. В конце концов, нужен ли tar флаг для каждого из форматов сжатия? Почему бы нам не использовать отдельный разархиватор и конвейер, именно так, как явно предполагается в философии Unix?

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

Вы реально «нарушаете» философию Unix лишь в случае, когда пишете шелл-утилиты, работающие не так, как все прочие.

Но вот чем вы рискуете, слишком увлекаясь подходом «делать всего одну вещь». Вы рискуете замкнуться в программистском восприятии проблемы «заточенном на фичи», а не рассматривать проблему как «ориентированную на решение задач», как ее видит пользователь. В самом деле, никому никогда не следует критиковать tar zxvf за выполнение задачи, которая в большинстве случаев стоит перед каждым, обращающимся к tar : файл tar.gz нужно разархивировать. Взять наиболее типичный случай, с которым придется иметь дело пользователю, после чего не решить его проблему напрямую, а потребовать, чтобы пользователь сразу потянулся за более сложной комбинацией инструментов – в любом случае, так себе дизайн.

Разумеется, было бы хорошо, если бы также не требовалось запоминать флаги вроде zxvf .

“Всё — файл”

Хотя я и думаю, что многие считают этот тезис элементом философии Unix, на самом деле формулировка “всё – файл” это отдельная идея. Вероятно, она приобрела некоторую известность, поскольку активно задействовалась при проектировании Plan 9. Философия Unix касается композиции, а область применения этой идеи более узка.

В частности, философия Unix предполагает, что утилиты должны потреблять и/или производить потоки текста. Это полезно потому, что так удается использовать больше утилит в связке друг с другом. Для того, чтобы из кусочков составлялось более крупное единство, кусочки должны быть хорошо подогнаны друг к другу.

Но идея “всё — файл” во многом этому противоречит. Она не столько о композиции, сколько о многоразовости. В данном случае суть в том, что у нас есть отдельный интерфейс с общим набором операций (открыть/закрыть/прочитать/записать), и мы можем взаимодействовать с интерфейсом на уровне этих операций. Мотивация, заложенная во «всё – файл» попросту такова: у нас есть широчайший набор утилит для операций над интерфейсом, поэтому для любой операции, влезающей в наш интерфейс, мы получаем все эти утилиты бесплатно. А важнее всего, что нам не придется изучать иной набор утилит. Можно шире применять имеющиеся у вас знания.

Философия Unix никоим образом не требует от нас брать на вооружение тезис «всё — файл». Мы вполне можем работать и с нефайловыми интерфейсами; все, что от нас при этом требуется – создавать утилиты, которые работали бы с интерфейсами конвенциональным образом. Притом, что Plan 9 может претендовать на некоторую идеологическую чистоту, поскольку эта ОС предоставляла сеть приложениям при помощи файловой системы /net , мы с тем же успехом можем добиться под POSIX некоторых аналогичных вещей при помощи netcat . Это не столь критично для того, чтобы с пользой применять оболочку.

Упс, но такого ведь не должно быть

При этом подход, при котором всё расценивается как файлы, сам не лишен недостатков. Еще в 2016 мы ввязались в одном полурегулярном опенсорсном сообществе в холивар, сводящийся к тому, что rm -rf / угробил мой компьютер. После того, как мы бросили работать с излюбленной всеми системой init, оказалось, что суть проблемы была в “efivars”, файловой системе для предоставления переменных системной прошивки. Если прошивка была с багами, то при удалении некоторых переменных систему становилось невозможно загрузить, и она оказывалась в невосстановимом виде.

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

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

Да это же простейшая возможность представить дерево

Тем временем, Linux продолжает доносить информацию до пользовательского пространства при помощи sysfs ; такая практика началась после того, как procfs показал себя не вполне подходящим, чтобы дампить в него такие интерфейсы. Но это также выглядит довольно подозрительно. Главное обоснование, почему sysfs спроектирован именно так, таково: ядро должно сообщать в пользовательское пространство данные, организованные в древовидной форме.

Казалось, что это естественно реализовать в виде файловой системы… объекты – это каталоги, ключи – это файлы, значения – это содержимое файлов, а массивы – это просто пронумерованные подкаталоги. Это и кажется подозрительным. Может быть, мы злоупотребляем файловой системой как таковой, чтобы представить древовидные данные, поскольку у нас нет возможности просто… передавать эти данные в виде дерева? Может быть, и так.

Это и наблюдается. На самом деле, правильно использовать /sys напрямую достаточно сложно, поэтому мы все равно постоянно прибегаем к инструментам вроде lspci , а файловая система как таковая нас уже практически не волнует. Таким образом, зачем же беспокоиться о файловой системе? При этом, придерживаясь работы с файловой системой, вы приобретаете проблемы, если вам требуется выполнять атомарные транзакционные изменения во множестве файлов сразу. Удачи вам.

Помню, однажды читал о данных, представляемых в виде файлов в /proc или /sys , но вообще их, как правило, сложно читать и понимать. Проблема в следующем: поскольку система постоянно меняется, и поскольку сложно считывать значения сразу из многих файлов, непротиворечивого ответа вы никогда не получите. Вы можете считать одно значение, затем система изменится, и вы там же прочитаете уже другое значение. Результат получается бессмысленным: например, отрицательные значения там, где величина обязательно должна быть положительной, либо расход ресурсов свыше 100%, в таком роде.

Работаем не только с текстовыми потоками

Когда Microsoft, наконец, изрядно надоело, что администрирование машин с Windows превращается в такой ужас, компания выдала блестящее решение: язык PowerShell. В нем вполне достойно воспроизведены лучшие черты философии Unix.

Определенно, у него есть и недостатки. Например, почти от всех старых программ Powershell отличается тем, что, для проведения каких-либо операций на нем сначала нужно написать специальный “cmdlet”. Соответственно, не так он и универсален, хотя, на нем все равно можно выполнять любые процессы и передавать любые аргументы командной строки.
Но с композиционной точки зрения PowerShell в основном поддерживает все тот же дизайн, что и Unix, но работает с потоками объектов, а не с потоками текста. Он дает приемлемую интероперабельность с текстовыми потоками, поскольку выполняет некоторые неявные преобразования (одно из них применяется почти всегда, с его помощью результаты отображаются в консоли).

В общем и целом, я бы назвал PowerShell грандиозным успехом. Он вынужденно замахивается на дизайн, более сложный, чем в Unix (в конце концов, ему приходится работать с уже существующими Windows API), но при этом предоставляет и дополнительные возможности, благодаря которым такая сложность приемлема. Powershell вполне приспособлен для работы с древовидными данными, пусть и в объектном преломлении.

Тем временем, философия «всё – файл» здесь и не просматривается. Поскольку все сущности являются объектами, вы освобождаетесь от необходимости иметь всего один файловый интерфейс. Напротив, теперь можно работать с объектами, реализующими почти любые интерфейсы. Такой подход более сложный, но и более мощный, и иногда стоит пойти на такой компромисс.

Есть и другие проблемы с тем, как спроектирован PowerShell, но, думаю, его можно охарактеризовать как «философия Unix: избранное и лучшее», а дополнительная сложность, присущая этому языку, нужна для обеспечения совместимости. Композиция – исключительно полезное средство при проектировании системы такого рода. Вторичная цель – обеспечить многоразовое использование. Философия «всё – файл» в рамках проектируемой нами системы нисколько не сравнится в полезности с более фундаментальной возможностью самостоятельно сформировать полезный инструментарий.

Композиция – это работа с типами

Чтобы скомпоновать определенные вещи, необходимо знать, как они стыкуются, а значит – нужно работать с типами. Начнем с того, каков тип базового элемента, например, процесса с его окружением, аргументами командной строки, stdin, stdout, stderr и кодом возврата. Процесс – это и есть базовая единица, которой мы собираемся оперировать.

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

При обращении с типичными оболочками эта работа настолько выходит за пределы работы с каналами | , что даже голова кружится. Работая с кодами возврата, можно реализовать логику && и || . Можно захватывать вывод в виде переменных при помощи $(cmd) . Можно выполнять переадресацию в файлы или создавать временные каналы также в виде «файлов» при помощи >(cmd) , так, чтобы файлы могли переадресовываться командам обратно, даже без записи на диск. И т. д.

# paste принимает имена файлов в качестве аргументов $ paste <(echo -e "a\nb\nc") <(echo -e "q\nw\ne") a q b w c e 

Итак, обрисовав всевозможные способы компоновки элементов, давайте попытаемся поработать с такой системой. Со временем мы соберем стандартную библиотеку из всех этих маленьких заготовок, которые всегда должны быть у нас под рукой. (Не бойтесь рефакторинга, ведь до того, как эти API превратились в границы системы, процесс постепенной доводки в стиле agile в значительной степени был определяющим на раннем этапе разработки Unix-оболочки). В конце концов, занимаясь компоновкой более крупных элементов, будем все чаще обнаруживать, что у нас уже есть готовые мелкие элементы для их сборки.

Трактуя композицию именно таким образом, в терминах компоновки операторов и тех типов, что они принимают и производят, неудивительно делать акцент на древовидных данных как на концепции, недооцененной во многих языках программирования. Почти любая композиция приводит к созданию дерева (или, как минимум, ориентированного ациклического графа, реже – просто графа). Это одна из основополагающих структур, которые мы должны быть в состоянии представлять.

В самом деле, несмотря на то, что философия Unix десятилетиями дает нам аккуратные маленькие оболочки, приспособленные для композиции, есть и мейнстримовые языки вроде Java. В них до самого 2014 года не было предусмотрено возможности компоновать мелкие операции подобным образом, пока не появились потоковые API.

Знаете, иногда я задумываюсь, а почему мы такие странные?

UNIX-way

UNIX-way подразумевает текстовый протокол взаимодействия между программами. Причём взаимодействие осуществляется обычно через безымянные каналы. Если можно так выразится, «отвязанность» от имен каналов нужна для того, чтобы можно было безболезненно разрывать и соединять программы в любые мыслимые цепочки. Этот подход рождает гибкость UNIX-а.

Вообще, UNIX-way подразумевает, что:

1. Любая программа — это монолитный качественный кубик.
2. Каждый кубик имеет один вход и два выхода. Один выход для результатов работы, а второй — для того чтобы сигнализировать о проблемах в работе кубика.
3. Мы можем соединять эти кубики в цепочки, наподобие бусинок на нитке.
4. В цепочке выход одного кубика подключается ко входу другого.
5. При необходимости между любыми двумя кубиками мы можем установить другие кубики.

Протокол «общения» между кубиками должен быть рассчитан на то, что любые кубики будут однозначно понимать входящие данные. Самый простой случай «общения» — это обмен данными в текстовом виде.

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

Преимущества текстового обмена нельзя скидывать со счетов. Текст — он и в Африке текст! Если что-то не получается, можно вывод направить на консоль, на принтер, в файл — да, куда угодно! И всегда есть уверенность, что текст можно будет прочитать и при необходимости подправить.

Текстовый формат нельзя недооценивать. Текстовый формат обмена — это огромное преимущество!

Допустим, вы пишите какую-то программу по обработке экспериментальных данных. Но сама установка, которая будет генерировать эти данные, еще не готова. Но вам уже нужно как-то отлаживать вашу прогу.

Если вы не приняли UNIX-way и решили пойти по пути двоичного обмена, то вам придется написать дополнительную программу, которая будет эмулировать выход экспериментальной установки. А если ваша прога отвечает UNIX-way, то вы просто создаете обычный текстовый файл и тупо скармливаете его своей программе.

Теперь, если окажется, что данные нужно слегка изменить, то в первом случае вам придется править исходники программы-эмулятора, а заново ее компилировать. А во втором случае — вы просто отредактируете текстовый файл. Где быстрее и проще достичь результата?

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

Компам ведь всё равно. Они ведь не страдают от того, что загружены работой. Я даже больше скажу — компы вообще не загружены работой!

Не верите ? — Посмотрите на занятость процессора своего компа!

Вы можете возразить, что текстовый формат занимает в разы больший объем памяти по сравнению с двоичным форматом. Да это так. Но разве кто-то запрещает использовать компрессоры-декомпрессоры?

Компрессор — это точно такой же кубик на нитке. Вы скармливаете поток текстовых данных компрессору, а вывод его направляете в файл. В результате Вы получаете сжатый файл. Разве это трудно сделать?

С таким же успехом вы можете подключить выход компрессора к каналу передачи данных. А на другом конце канала установите декомпрессор. Тогда на выходе декомпрессора вы получите восстановленный поток текстовых данных. А по каналу связи побежит сжатый поток двоичных данных.

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

Не пренебрегайте текстовым форматом данных! В нем заложены очень большие возможности для маневров. Сколько раз в вашей жизни ломались какие-нибудь двоичные файлы? (Например, Реестр в Виндовсе.) С какой вероятностью его можно было восстановить и как ни в чем не бывало продолжить работу? А сколько раз в вашей жизни ломались текстовые файлы?

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

И если вы считаете, что это не так, то это значит, что вы еще не «попадали» по-настоящему.

Что такое unix way

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

Наиболее точно охарактеризовать то, о чем пойдет речь можно лишь процитировав одного из основателей традиций Unix и разработчика технологии под названием "Unix pipes" - Douglas'а Mcllroy'а:

"This is the Unix philosophy: - Write programs that do one thing and do it well. - Write programs to work together. - Write programs to handle text streams, because that is a universal interface."

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

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

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

Закончив лирическое отступление, хочется взглянуть на нашу философию с точки зрения программиста.

Взгляд с точки зрения программиста

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

Как же это выглядит?

Одна задача - одна программа

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

Подобное множество программ решающих элементарные задачи делает количество способов решения какой-либо комплексной задачи стремящимся к бесконечности, ведь при наличии стандартизованного интерфейса комбинировать программы можно в любой последовательности. Для расширения возможностей такого рода комбинирования используются различные скриптовые языки, которых существует достаточно много, наиболее распространенным из которых являются bash скрипты, основанные на командах одноименной оболочки командной строки, используемой по-умолчанию во всех (хотя возможно стоило не использовать громких слов и написать "в большинстве") дистрибутивах Linux.

Unix pipes

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

На подробностях реализации останавливаться не будем, по этому позволю себе плавно перейти к следующему разделу и продолжить эту тему уже там.

Взгляд с точки зрения пользователя

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

У каждой элементарной программы, соответствующей этой идеологии, должен быть входной и выходной стандартные текстовые потоки - stdin и stdout соответственно. Механизм unix pipes позволяет перенаправлять эти потоки любой программы произвольным образом с помощью трех простых операторов: | , > и < . Первый из них - | перенаправляет stdout команды слева от него в stdin команды справа, а >и < предназначены для перенаправление потоков в/из файлы по схожему принципу.

Предлагаю рассмотреть этот механизм на примерах. Возьмем несколько базовых утилит, имеющихся на практически любой unix-like системе:

  • cat - вывод содержимого указанного первым параметром файла в stdout (по умолчанию stdout в большинстве программ направляется в консоль)
  • less - постраничный вывод текста, полученного в stdin в stdout (переключение страниц и некоторые другие функции производятся с клавиатуры, возможны и другие варианты использования, но они нам не нужны)
  • grep - построчная фильтрация текста, полученного в stdin, вывод только строк, содержащих текст, указанный первым аргументом, и вывод результата в stdout.

Начнем с примера, позволяющего прочитать постранично любой файл:

cat readme.txt | less

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

cat readme.txt | grep unix > readme.txt

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

cat readme.txt | grep unix > meread.txt

Если же Вы хотите избежать очищения файла, в который производится запись, необходимо написать символ > дважды, тогда новые данные припишутся в конец:

cat readme.txt | grep unix >> readme.txt

В unix-like системах есть еще одна интересная особенность, косвенно связанная с этим механизмом: все устройства являются файлами и соответственно, прикреплены к файловой системе, для них выделена отдельная директория, по традиции называемая /dev . Работа с ними также ведется на тех же правах что и с обычными файлами, например набрав в консоли:

cat readme.txt > /dev/dsp

в ответ от компьютера Вы услышите некоторый звук, издаваемый из колонок или наушников.

Подводим итоги

С точки зрения простого пользователя использование opensource решений, построенных на базе философии unix, является как минимум нетривиальной задачей - ведь от него требуется как минимум понимание насколько мощная и гибкая система попала ему/ей в руки. Отсутствие единственного верного способа решения той или иной задачи ставит большинство людей попросту в тупик, у них начинают разбегаться глаза от десятков тысяч программ, доступа к которым есть у всех пользователей unix-like операционных систем, с помощью набора простой волшебной команды в консоли, состоящей не более чем из трех-четырех слов.

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

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *