Эволюция оболочек командной строки для платформы Linux

От Bourne shell к Bash shell и далее

Графический интерфейс пользователя стал стандартным способом выполнения повседневных задач, но для того, чтобы в полной мере воспользоваться преимуществами Linux перед другими платформами, определенно стоит освоить работу в командной строке через соответствующие оболочки. Сегодня существует множество оболочек для командной строки, начиная от Bash и Korn до C shell и заканчивая совсем уж экзотическими оболочками. Эта статья поможет вам выбрать оболочку, лучше всего подходящую под ваши задачи.

M. Тим Джонс, инженер-консультант, Emulex Corp.

М. Тим ДжонсМ. Тим Джонс - архитектор встроенного ПО и, кроме того, автор книг Artificial Intelligence: A Systems Approach, GNU/Linux Application Programming (выдержавшей на данный момент второе издание), AI Application Programming (второе издание) и BSD Sockets Programming from a Multilanguage Perspective. Он имеет обширный опыт разработки ПО в самых разных предметных областях - от ядер специальных ОС для геосинхронных космических аппаратов до архитектур встраиваемых систем и сетевых протоколов. Тим - инженер-консультант Emulex Corp., Лонгмонт, Колорадо.



14.10.2012

Связаться с Тимом

Тим – один из самых популярных и активных авторов портала developerWorks. Ознакомьтесь с другими статьями Тима и посетите его страницу, чтобы связаться с ним или другими авторами и участвовать в жизни сообщества developerWorks.

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

Мое первое знакомство с современной оболочкой произошло в 80-ых годах, когда я разрабатывал ПО для SunOS. После того как я научился использовать информацию, выводимую одной программой, в качестве входных данных для другой программы (и даже делать это несколько раз в виде цепочки), я получил простой и эффективный способ создавать фильтры и преобразования. Базовая идея состояла в том, чтобы предоставить способ создавать простые инструменты, которые были бы достаточно гибкими, чтобы объединяться с другими инструментами для выполнения полезных действий. Благодаря этому оболочки не только обеспечили взаимодействие с ядром и устройствами, но и интегрировали в себя службы (например, каналы и фильтры), которые сейчас являются стандартными шаблонами проектирования из области разработки ПО.

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

История оболочек

Оболочка командной строки как язык программирования

Оболочки командной строки – это специализированные, ориентированные на конкретную область языки программирования (малые языки), реализующие определенную модель использования (в данном случае - предоставляющие интерфейс к операционной системе). Помимо текстовых оболочек для операционных систем, можно встретить оболочки и с графическим интерфейсом пользователя, а также специализированные оболочки для языков программирования (например, оболочка irb для языка Ruby). Принцип, лежащий в основе оболочки командной строки, также применяется для поиска в Web с помощью Web-интерфейса goosh. Эта оболочка поверх поисковой машины Google позволяет осуществлять поиск в Google из командной строки c помощью команд search, more и т.д.

Оболочки (или интерпретаторы) командной строки имеют длинную историю, но мы начнем обсуждение с первой оболочки для Unix. Кен Томпсон (Ken Thompson) из компании Bell Labs в 1971 году разработал первую оболочку для Unix® – V6 shell. Так же как и ее предшественники для ОС Multics, эта оболочка (/bin/sh) была независимой пользовательской программой, выполняющейся за пределами ядра. Такие идеи, как глоббинг (globbing - использование шаблонов для расширения списка допустимых значений, например *.txt), были реализованы в виде отдельной утилиты glob; также существовала отдельная команда if для вычисления условных выражений. Подобное разделение функциональности позволило уменьшить размер оболочки, так что она занимала всего 900 строк кода на языке С (в разделе "Ресурсы" приведена ссылка на оригинальный исходный код данной оболочки).

В этой оболочке был впервые представлен краткий синтаксис для перенаправления (< > и >>) и конвейерной обработки (| или ^), который используется и в современных оболочках. Также в ней можно обнаружить поддержку последовательного вызова нескольких команд (с помощью ;) и асинхронных команд (с помощью &).

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


Развитие оболочек командной строки UNIX после 1977 г.

После знакомства с оболочкой Томпсона можно обратиться к современным оболочкам, начиная с 1977 года, когда была представлена оболочка Борна (Bourne shell). Эта оболочка, разработанная Стивеном Борном (Stephen Bourne) из компании AT&T Bell Labs для UNIX V7, и сегодня сохраняет свою актуальность (иногда используясь в качестве стандартной оболочки для пользователя root). Стивен разрабатывал Bourne shell после работы над компилятором ALGOL68, так что грамматика, используемая в Bourne shell, гораздо больше похожа на ALGOL, чем в других оболочках. В исходном коде оболочки, также написанном на языке С, даже используются макросы, что делает его еще больше похожим на ALGOL68.

Оболочка Борна выполняла две основные задачи:

  • служила интерпретатором команд, позволяя выполнять команды в интерактивном режиме;
  • позволяла создавать сценарии, которые можно было неоднократно вызывать с помощью оболочки.

Кроме замены оболочки Томпсона, оболочка Борна предоставляла несколько преимуществ, отсутствовавших у её предшественниц. Так, в ней появилось управление потоком выполнения, циклы и переменные для сценариев; был представлен более функциональный язык для взаимодействия с системой (в интерактивном и неинтерактивном режимах). Оболочка также позволяла использовать сценарии в качестве фильтров, обеспечивая поддержку обработчиков сигналов от операционной системы, но не имела возможности определять функции. Наконец, в ней появилось несколько возможностей, используемых и сегодня, например подстановка команд (с помощью обратных кавычек) и встроенных документов (HERE document) для встраивания строковых констант в сценарии.

Оболочка Борна не только представляла собой важный шаг вперед, но и послужила отправной точкой для многочисленных оболочек, последовавших за ней, которые и сегодня используются в стандартных Linux-системах. На рисунке 1 приведено "генеалогическое дерево" основных оболочек. От Bourne shell происходят Korn Shell (ksh), Almquist shell (ash) и известной оболочки Bash (Bourne Again Shell). Оболочка C Shell (csh) уже находилась в разработке к моменту появления Bourne shell. На рисунке 1 показаны основные направления развития, но не показаны все «перекрестные» связи между оболочками, которые иногда оказывали значительное влияние на ту или иную оболочку.

Рисунок 1. Развитие оболочек командной строки Linux с 1977 года
Рисунок 1. Развитие оболочек командной строки Linux с 1977 года

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


Базовая архитектура оболочки командной строки

Фундаментальная архитектура гипотетической оболочки довольно проста (как можно увидеть на примере оболочки Борна). Как показано на рисунке 2, базовая архитектура оболочки напоминает контейнер, в котором происходят следующие процессы:

  • лексический анализ и "разбор" входных данных;
  • "раскрытие" символов (с помощью различных методов, таких как скобки, символ тильды (~), развертывание и замена значений и параметров, генерация имен файлов);
  • выполнение команд пользователя (с использованием команд, встроенных в оболочку, и внешних команд).
Рисунок 2. Простая архитектура типичной оболочки командной строки
Рисунок 2. Простая архитектура типичной оболочки командной строки

В разделе "Ресурсы" можно найти ссылки на статьи об архитектуре open-source оболочки Bash shell.


Знакомство с оболочками Linux

Теперь познакомимся с некоторыми оболочками, чтобы узнать об их возможностях и изучить примеры сценариев для различных оболочек. В данном обзоре мы рассмотрим следующие оболочки: C shell, Korn shell и BASH.

The Tenex C shell

Оболочка C (C shell) была разработана Биллом Джоем (Bill Joy) в 1978 для BSD (Berkeley Software Distribution) UNIX-систем, когда он был аспирантом в Университете Калифорнии в Беркли. Спустя пять лет в эту оболочку была добавлена функциональность из операционной системы Tenex, использовавшейся на системах DEC PDP. Из Tenex были позаимствованы автоматическое завершение имен файлов и команд и функциональность для редактирования содержимого командной строки. Оболочка tcsh (Tenex C shell) осталась обратно совместимой с csh, но значительно усовершенствовала интерактивные возможности оболочки C. Оболочка tcsh была разработана Кеном Гриром (Ken Greer) в Университете Карнеги-Меллона.

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

Полезной возможностью, добавленной Биллом в оболочку C, была и история команд. Эта функциональность поддерживала историю ранее выполненных команд и позволяла пользователю просматривать этот список и выбирать для повторного запуска различные команды, уже запускавшиеся ранее. Например, если ввести команду history, будет выведен список ранее выполненных команд. Можно выбрать команду в этом списке с помощью клавиш "вверх" и "вниз" или выполнить предыдущую команду, введя !!. Также можно ссылаться на аргументы, переданные предыдущим командам, например, !* ссылается на все аргументы предыдущей команды, а !$ ссылается на последний аргумент предыдущей команды.

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

Сценарий для tcsh состоит из трех основных разделов. Последовательность символов #! в самом начале сценария помечает данный файл как интерпретируемый, чтобы впоследствии он мог бы быть выполнен указанной оболочкой (в данном случае – tcsh). Это позволяет запустить данный файл как обычный исполняемый файл и не указывать перед ним исполняемый файл интерпретатора. В сценарии используется счетчик исполняемых файлов, поэтому изначально значение этого счетчика инициализируется нулем.

Листинг 1. Tcsh-сценарий для поиска всех исполняемых файлов в каталоге
#!/bin/tcsh
# найти все исполняемые файлы

set count=0

# проверить аргументы, переданные на вход
if ($#argv != 1) then
  echo "Usage is $0 <dir>"
  exit 1
endif

# проверить, что значение аргумента указывает на каталог
if (! -d  $1) then
  echo "$1 is not a directory."
  exit 1
endif

# пройти по каталогу, выводя названия исполняемых файлов
foreach filename ($1/*)
  if (-x $filename) then
    echo $filename
    @ count = $count + 1
  endif
end

echo
echo "$count executable files found."

exit 0

В первом разделе проверяются аргументы, переданные пользователем. Переменная #argv представляет всё переданные аргументы (за исключением самого названия команды). К этим аргументам можно обращаться, указав их индекс, например, #1 указывает на первый аргумент (это сокращенная форма записи для argv[1]). Наш сценарий ожидает только один аргумент, и если он его не находит, выводится сообщение об ошибке. В этом сообщении используется #0 (сокращенная форма записи для argv[0]) для вывода имени команды, которое было введено пользователем в консоль.

Во втором разделе выполняется проверка того, что переданный аргумент указывает на каталог. Оператор –d возвращает true, если значение аргумента соответствует каталогу. Но заметьте, что первым я указываю символ !, который означает отрицание. Подобное выражение означает "если аргумент не является каталогом, вывести сообщение об ошибке".

В последнем разделе выполняется итерирование по файлам, которые находятся в каталоге, чтобы определить, какие из них являются исполняемыми. Для этого используется удобный оператор foreach, который последовательно извлекает элементы из списка, находящегося в круглых скобках (в данном случае – из каталога), а затем проверяет каждый элемент. Для проверки того, является файл исполняемым или нет, используется оператор –x; если файл исполняемый, его название печатается в консоль, а счетчик увеличивается на единицу. В конце сценария печатается общее количество исполняемых файлов.

Оболочка Корна

Оболочка Korn shell (ksh), спроектированная Дэвидом Корном (David Korn), была представлена в тоже время, что и оболочка Tenex С. Одной из интересных возможностей этой оболочки является язык сценариев, кроме того, она обладает обратной совместимостью с оригинальной оболочкой Борна.

Оболочка Корна была коммерческим продуктом до 2000 года, когда она была выпущена под Open Source-лицензией CPL (Common Public License). Кроме отличной совместимости с Bourne shell, оболочка Корна включала функциональность и из других оболочек (например, историю команд из csh). В оболочке Корна также присутствовало несколько дополнительных возможностей, например, ассоциативные массивы и вычисления с плавающей точкой, которые увидеть можно в современных языках сценариев, такие как Ruby и Python. Эта оболочка доступна для различных операционных систем, включая IBM® AIX® и HP-UX, и старается обеспечить поддержку POSIX-стандарта для языков оболочек (Portable Operating System Interface for UNIX - интерфейс переносимой операционной системы для UNIX-платформ).

Оболочка Корна основана на оболочке Борна, поэтому она больше похожа на Bourne shell и Bash, чем на оболочку C. В листинге 2 приведен пример сценария для оболочки Корна, в котором также реализован поиск исполняемых файлов.

Листинг 2. Ksh-сценарий для поиска исполняемых файлов
#!/usr/bin/ksh
# найти все исполняемые файлы

count=0

# проверить входные параметры
if [ $# -ne 1 ] ; then
  echo "Usage is $0 <dir>"
  exit 1
fi

# убедиться, что значение параметра соответствует каталогу
if [ ! -d  "$1" ] ; then
  echo "$1 is not a directory."
  exit 1
fi

# пройти по каталогу и вывести список исполняемых файлов
for filename in "$1"/*
do
  if [ -x "$filename" ] ; then
    echo $filename
    count=$((count+1))
  fi
done

echo
echo "$count executable files found."

exit 0

Как можно заметить, листинг 2 очень похож на листинг 1. Структура обоих сценариев практически идентична, но заметны важные различия в условных операторах, выражениях и итерировании. Вместо C-подобных операторов в оболочке ksh применяются операторы, используемые в оболочке Борна (-eq, -ne, -lt).

В оболочке Корна также существуют определенные отличия, связанные с процессом итерирования. В ней используется структура for in, в которую подставляется результат команды ls '$1/*, возвращающей список файлов, содержащихся в указанном подкаталоге.

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

The Bourne-Again Shell

Оболочка Bash (Bourne-Again Shell) – это open-source проект сообщества GNU, призванный заменить оболочку Борна. Эта оболочка, разработанная Брайаном Фоксом (Brian Fox), стала одной из самых распространенных (она присутствует на платформах Linux, Darwin, Windows, Cygwin и т.д.). Как очевидно из её названия (Bourne-Again Shell можно прочесть как "возрожденная оболочка Борна"), оболочка Bash является "расширением" оболочки Борна, и большинство сценариев для оболочки Борна можно без изменений запускать в оболочке Bash.

Кроме обеспечения обратной совместимости для сценариев, оболочка Bash включает в себя возможности из оболочек Корна и C. В ней есть история команд, редактирование в командной строке, стек каталогов (команды pushd и popd), множество полезных переменных среды, автоматическое завершение команд и другие возможности.

Оболочка Bash продолжает развиваться, получая новые возможности, например, поддержку регулярных выражений (как в языке Perl) и ассоциативных массивов. Хотя некоторые из этих возможностей могут быть недоступны в других языках сценариев, всё равно можно писать сценарии, совместимые с другими языками. Поэтому пример сценария из листинга 3 практически полностью совпадает со сценарием для оболочки Корна из листинга 2, за исключением пути к исполняемому файлу оболочки - /bin/bash.

Листинг 3. Bash-сценарий для поиска исполняемых файлов
#!/bin/bash
# найти все исполняемые файлы

count=0

# проверить входные параметры
if [ $# -ne 1 ] ; then
  echo "Usage is $0 <dir>"
  exit 1
fi

# убедиться, что значение параметра соответствует каталогу
if [ ! -d  "$1" ] ; then
  echo "$1 is not a directory."
  exit 1
fi

# пройти по каталогу и вывести список исполняемых файлов
for filename in "$1"/*
do
  if [ -x "$filename" ] ; then
    echo $filename
    count=$((count+1))
  fi
done

echo
echo "$count executable files found."

exit 0

Одним из ключевых различий между этими оболочками являются лицензии, под которыми они распространяются. Оболочка Bash, разработанная в рамках проекта GNU, по естественным причинам выпущена под лицензией GPL. Оболочки csh, tcsh, zsh, ash и scsh были выпущены под лицензией BSD или аналогичной. Оболочка Корна распространяется под лицензией CPL.


Экзотические оболочки

Для интересующихся пользователей доступны альтернативные оболочки, которые также можно использовать в зависимости от конкретных задач или предпочтений. Оболочка scsh (Scheme Shell) предлагает среду для создания сценариев, использующую язык Scheme (развитие языка Lisp). В оболочке Pyshell попытались создать среду для разработки сценариев, аналогичную языку программирования Python. Для встраиваемых систем имеется пакет BusyBox, в котором оболочка и необходимые команды упакованы в один бинарный файл, что облегчает распространение и настройку.

В листинге 4 представлен пример сценария для поиска исполняемых файлов для оболочки scsh. Хотя вид этого сценария может показаться незнакомым, в нем реализована такая же функциональность, как и в сценариях, разбиравшихся ранее. В этом сценарии присутствуют три функции и непосредственно выполняющийся код (он расположен внизу сценария) для проверки переданных аргументов. Основной код сценария находится в функции showfiles, которая выполняет итерирование по списку (созданному после вызова with-cwd) и вызывает функцию write-ln для каждого элемента списка. Сам список создается путем итерирования по содержимому заданного каталога и отбора файлов, которые являются исполняемыми.

Листинг 4. Scsh-сценарий для поиска исполняемых файлов
#!/usr/bin/scsh -s
!#

(define argc
        (length command-line-arguments))

(define (write-ln x)
        (display x) (newline))

(define (showfiles dir)
        (for-each write-ln
                (with-cwd dir
                        (filter file-executable? (directory-files "." #t)))))

(if (not (= argc 1))
        (write-ln "Usage is fae.scsh dir")
        (showfiles (argv 1)))

Заключение

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

Ресурсы

  • Evolution of shells in Linux: оригинал статьи (EN).
  • The V6 Thompson Shell Port (osh): на этом Web-сайте, разрабатываемом и поддерживаемом Дж. А. Нейтцелем, опубликован исходный код оболочки osh и внешних утилит (таких как if и goto), используемых ею. Здесь же можно найти исходный код оболочки Томпсона и загрузить архив с исходным кодом утилит, написанных для этой оболочки.
  • Goosh: неофициальная оболочка от Google, предоставляющая интерфейс оболочки для стандартного поискового интерфейса Google. Goosh является интересным примером того, как можно применять оболочки для нетрадиционных интерфейсов.
  • Оболочка Борна является "прародительницей" всех существующих на данный момент оболочек. Ее исходный код во многом следует стилю, принятому в ALGOL68, например, в ней используется макросы на языке C.
  • Bourne-Again Shell: эта оболочка, объединяющая возможности Bourne shell, Korn shell и C shell, встречается на Linux-платформах чаще всего. Прочитайте третью главу книги "The Architecture of Open Source Applications", в которой описывается структура и аспекты реализации Bash.
  • Ознакомьтесь с другими статьями на портале developerWorks, в которых описывается создание сценариев для оболочек командной строки:
  • Kornshell: на этом Web-cайте можно узнать последние новости об оболочке Корна, включая документацию и другие ресурсы.
  • В Википедии имеется превосходное сравнение различных оболочек, включая общие характеристики, интерактивные возможности, возможности для программирования, синтаксис, типы данных и IPC-механизмы.
  • BusyBox simplifies embedded Linux systems (developerWorks, август 2006 г.) (EN): ещё одна статья Тима, знакомящая с пакетом BusyBox, в которой рассказывается, как добавлять новые команды в архитектуру этой статичной оболочки.
  • Следите за публикациями Тима и developerWorks в Твиттере или подпишитесь на канал твитов по Linux на developerWorks.

Комментарии

developerWorks: Войти

Обязательные поля отмечены звездочкой (*).


Нужен IBM ID?
Забыли Ваш IBM ID?


Забыли Ваш пароль?
Изменить пароль

Нажимая Отправить, Вы принимаете Условия использования developerWorks.

 


Профиль создается, когда вы первый раз заходите в developerWorks. Информация в вашем профиле (имя, страна / регион, название компании) отображается для всех пользователей и будет сопровождать любой опубликованный вами контент пока вы специально не укажите скрыть название вашей компании. Вы можете обновить ваш IBM аккаунт в любое время.

Вся введенная информация защищена.

Выберите имя, которое будет отображаться на экране



При первом входе в developerWorks для Вас будет создан профиль и Вам нужно будет выбрать Отображаемое имя. Оно будет выводиться рядом с контентом, опубликованным Вами в developerWorks.

Отображаемое имя должно иметь длину от 3 символов до 31 символа. Ваше Имя в системе должно быть уникальным. В качестве имени по соображениям приватности нельзя использовать контактный e-mail.

Обязательные поля отмечены звездочкой (*).

(Отображаемое имя должно иметь длину от 3 символов до 31 символа.)

Нажимая Отправить, Вы принимаете Условия использования developerWorks.

 


Вся введенная информация защищена.


static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=40
Zone=Linux
ArticleID=839005
ArticleTitle=Эволюция оболочек командной строки для платформы Linux
publish-date=10142012