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








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

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

Оболочки


Автор: Денис Колисниченко, dhsilabs@mail.ru
Опубликовано: 31.07.2002
Оригинал: http://www.softterra.ru/freeos/19209/


Программы-оболочки.
Интерпретатор bash
Командный язык bash

  1. Переменные.
  2. Массивы.
  3. Специальные переменные.
  4. Арифметические выражения
  5. Подстановка переменных. Подстановка значений.
  6. Управляющие структуры.
  7. Циклы.
  8. Подоболочки

Программы-оболочки.

В той статье мы поговорим о неотъемлемой части пользовательского интерфейса Linux - о программах-оболочках. Именно программа-оболочка запускается после регистрации пользователя в системе. Программы-оболочки часто называют командными интерпретаторами, потому что они обрабатывают (интерпретируют) команды, введенные пользователем. Во времена DOS этим занимался файл C:\command.com.
В файле /etc/passwd для каждого пользователя указывается, какую оболочку он будет использовать.

root:x:0:0:root:/root:/bin/bash
den:x:501:501:Denis:/home/den:/bin/bash

Из листинга видно, что оба пользователя используют оболочку /bin/bash. Оболочка, как правило, указывается в последнем поле файла passwd.
Список всех установленных в системе программ-оболочек содержится в файле /etc/shells. У меня он выглядит так:

Листинг 1.

/bin/sh
/bin/bash
/bin/ash
/bin/bsh
/bin/tcsh
/bin/csh
/bin/zsh

На любой Unix-системе, даже на самой старой, вы можете увидеть, по крайней мере, два интерпретатора из этого списка: sh, csh. Названия, как вы догадались, исходят от слова shell - оболочка. Csh - это оболочка, использующая командный язык, напоминающий язык программирования C. В ОС Linux по умолчанию используется оболочка bash (Bourne Again Shell). Bash является более <продвинутой> версией обыкновенной оболочки sh.
Все оболочки выполняют одну и туже функцию - интерпретируют команды пользователя. Например, когда вы вводите команду

$ program -s /etc/passwd

Интерпретатор запустит программу program и передаст ей два (точнее три)параметра: первый - это -s, второй - это /etc/passwd. А что делать с этими параметрами разберется сама программа. Я что-то говорит о третьем параметре? Существует еще один так называемый нулевой параметр. Этот параметр содержит полное имя файла программы. Например, если наша программа находится в каталоге /bin, то нулевой параметр будет содержать значение /bin/program.
Обратите внимание на знак доллара возле команды. Это признак того, что сейчас мы работаем как обыкновенный пользователь. Если мы зарегистрируемся как пользователь root (суперпользователь), то знак доллара измениться на решетку - #.
Отличием каждой программы-оболочки является ее командный язык. Вот поэтому одни пользователи предпочитают использовать bash, а другие - tcsh. Командные языки некоторых оболочек очень похожи, например, sh и bash, csh и tsch.
Командный язык используется для создания сценариев. Сценарий - это последовательность команд, которую должен выполнить интерпретатор команд. Пример простейшего сценария (командный язык bash):

Листинг 2.

#!/bin/bash
echo -n "Enter your name"
read name
echo "Hello, $name"

Для определенности скажем, что мы сохранили этот сценарий под именем script1. Чтобы мы могли выполнить этот сценарий нужно сделать этот файл исполнимым (например, с помощью команды chmod 550 ./script1) и ввести команду (я предполагаю, что вы создали этот файл в текущем каталоге):
./script1

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

/bin/bash ./script1

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

#!/usr/bin/myshell -option

При запуске сценария, в первой строке которого стоит такая команда, интерпретатор выполнит команду:
/usr/bin/myshwll -option <имя_файла_сценария>

Обратите внимание на первую строку сценария script1
#!/bin/bash
Это указание программы для обработки этого сценария. Если же вы напишете
# !/bin/bash
то это будет уже обыкновенным комментарием и система не сможет выполнить ваш сценарий, поскольку не будет знать, какую программу-оболочку нужно использовать. В лучшем случае, если сейчас активным является интерпретатор, на языке которого написан сценарий, файл все-таки будет выполнен, а в другом случае вы получите сообщение об ошибке. Например, если вы написали сценарий на bash, не указав (или неправильно указав) интерпретатор, и пытаетесь выполнить его при запущенном интерпретаторе csh, то получите сообщение о синтаксической ошибке.

Интерпретатор bash

В этой статье я рассмотрю оболочку bash, которая стандартна для большинства систем. Однако сам командный язык подробно разобран не будет из-за его объемности. Итак, начнем по порядку с регистрации пользователя. Как я уже отмечал, после успешной аутентификации пользователя, запускается программа-оболочка (в нашем случае это /bin/bash). При запуске оболочки выполняются некоторые действия. Эти действия определены в файле ~/.bash_profile (см. листинг 3).

Листинг 3.

# .bash_profile
# Get the aliases and functions
if [ -f ~/.bashrc ]; then
 ~/.bashrc
fi
# User specific environment and startup programs
PATH=$PATH:$HOME/bin
BASH_ENV=$HOME/.bashrc
USERNAME="user"
HISTIGNORE=" [   ]*:&:bg:fg"
export USERNAME BASH_ENV PATH HISTIGNORE
clear

Файл .bash_profile представляет собой обыкновенный сценарий. Теперь разберемся, какие действия выполняет этот сценарий. В этом файле определяются переменные окружения и программы, которые должны запускаться автоматически. В листинге 3 сначала проверяется существование файла .bashrc и, если он существует, интерпретатор выполняет его. Файл .bashrc рассмотрим немного позже. Затем устанавливаются переменные окружения: PATH, BASH_ENV, USERNAME, HISTIGNORE. Первая задает путь для поиска программ, вторая определяет среду интерпретатора (файл .bashrc), третья устанавливает имя пользователя, а последняя относится к истории команд, введенных пользователем. Затем переменные экспортируются. Дело в том, что переменные локальны в рамках сценария. При экспорте переменных их значение будет доступно порожденным процессам. Например, создайте такие два сценария (см. листинги 4 и 5).

Листинг 4.

#!/bin/bash
MYVAR="My var"
export MYVAR
./listing5

Листинг 5

#!/bin/bash
echo "$MYVAR"

Запустите сценарий ./listing4. Он экспортирует переменную MYVAR и запустит сценарий listing5, которые выведет значение переменной MYVAR (<My var>) на экран. Теперь закомментируйте строку:
export MYVAR
в сценарии listing4 и запустите его снова. На экране значение переменной MYVAR не будет отображено.
Теперь вернемся к файлу .bashrc, а потом перейдем к командному языку оболочки bash.

Листинг 6

# .bashrc
# User specific aliases and functions
alias rm='rm -i'
alias mv='mv -i'
alias cp='cp -i'
alias s='cd ..'
alias d='ls'
alias p='cd -'
# Need for a xterm & co if we don't make a -ls
[ -n $DISPLAY ] && {
 [ -f /etc/profile.d/color_ls.sh ] && source /etc/profile.d/color_ls.sh
  export XAUTHORITY=$HOME/.Xauthority
}
# Read first /etc/inputrc if the variable is not defined, and after
the /etc/inputrc
# include the ~/.inputrc
[ -z $INPUTRC ] && export INPUTRC=/etc/inputrc
# Source global definitions
if [ -f /etc/bashrc ]; then
 . /etc/bashrc
fi

Здесь задаются определенные установки. В основном данные установки необходимы для удобства пользователя. Например, определяются псевдонимы команд (alias).
Оболочка bash использует еще один командный файл - .bash_logout. В этом файле указываются действия, которые нужно выполнить при выходе из интерпретатора, то есть выхода из системы.
 

Командный язык bash. Переменные.

Обязательным атрибутом переменной в любом языке программирования является тип значения переменной. В командном языке bash все переменные текстовые. Например, если вы присваиваете переменной значение A=23, то значением переменной будет строка из двух сиволов - <23>.
Имя переменной должно начинаться с буквы и может состоять из латинских букв, цифр, знака подчеркивания.
Оператор присваивания в bash выглядит так:
<имя переменной>=значение.
Например,

NAME=Ivan

Если нужно присвоить значение, содержащее пробелы, нужно использовать кавычки:

NAME="Ivan Ivanov"

Обращение к значению переменной выполняется с помощью знака доллара перед именем переменной:

echo "NAME"
echo "$NAME"

Первая команда выведет на экран слово NAME, а вторая - значение переменной NAME (Ivan Ivanov). Если значение переменной может содержать пробелы, имя переменной нужно заключить в кавычки. Например,

NAME="Ivan Ivanov"
echo $NAME
echo "$NAME"

Первая команда echo выведет на экран слово Ivan, а вторая - Ivan Ivanov.
Интерпретатор bash использует такие метасимволы, имеющее для него особое значение:
   * ? ; & ( ) | ^ < > <возврат_каретки> <табуляция> <пробел&qt;
Для того, чтобы использовать эти символы как они есть, нужно их цитировать с помощью символа \. Например, символ перевода строки можно цитировать так \n, символ табуляции - \t, символ вопроса - \?
Особое значение при присваивании переменным значений имеют кавычки. Все символы, заключенные в одинарные кавычки ' ' представляют самих себя. Между двойными кавычками " " выполняются команды и подстановки значений.
Символы "\", ",", " ' ", " $ " могут цитироваться с помощью обратной наклонной черты: \\, \$, \'

Массивы.

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

Имя_массива[индекс]=значение

Например

Array[1]=23
Array[3]=54
Array[0]=77

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

set -A array 3 56 77 12

Аналогично выражениям

array[0]=3
array[1]=56
array[2]=77
array[3]=12

Обратиться ко всем элементам массива сразу можно так:
${array[@]}, где array - имя массива.
Например, echo ${array[@]}
 

Специальные переменные.

Каждому процессу доступны переменные оболочки, приведенные в таблице 1.

Таблица 1
Специальные переменные

100%> align=center border=1>
Переменная Значение
HOME Домашний каталог
MAIL Имя файла, в который поступает электронная почта
LOGNAME Имя пользователя, которое использовалось для входа в систему
PATH Путь вызова
SHELL Имя интерпретатора команд
PWD Текущий каталог
UID Идентификатор пользователя, запустившего сценарий
RANDOM Случайное число в диапазоне от 0 до 32767
SECONDS Число секунд, прошедшее с момента запуска оболочки

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

Таблица 2.
 

100%> align=center border=1>
Переменная Значение
$0 Имя выполняемой команды. Для сценария - путь, указанный при его вызове. 
$1 Первый параметр, указанный при вызове сценария. Аналогично, $2 - второй, $n - n-ый параметр.
$# Число параметров, которые были указаны при вызове сценария.
$* Все параметры, заключенные в кавычки: "$1 $2 ..."
$? Код завершения последней команды
$$ Номер текущего процесса (PID)

Арифметические выражения

Подстановка арифметических выражений осуществляется с помощью конструкции

$(( выражение ))

Например,

N = $(( (10+5)/2 ))
echo $N

На экране вы увидите 3, а не 3,5, потому что интерпретатор bash использует целочисленные вычисления.
Количество часов, прошедшее с момента запуски оболочки можно вычислить так:
hrs = $(( $SECONDS/3600 ))
 

Подстановка переменных. Подстановка значений.

Интерпретатор bash предоставляет нам довольно гибкий механизм подстановки переменных. При этом переменная будет использоваться не всегда, а в зависимости от определенных обстоятельств. (см. таблицу 3).

Таблица 3.
 

100%> align=center border=1>
${переменная:=значение} Значение присваивается переменной, если она не определена или является пустой строкой.
${переменная:?сообщение} Если переменная не определена или является пустой строкой, выводится сообщение
${переменная:+значение} Если переменная инициализирована (определена), вместо нее используется указанное в конструкции значение. (*) 
${переменная} Если переменная определена, подставляется ее значение. Скобки используются лишь для того, если после переменной стоит символ, который может <приклеиться> к имени переменной.
${переменная:-значение} Если переменная определена и не является пустой строкой, подставляется ее значение, иначе подставляется значение, указанное в конструкции. (*)

(*) Реальное значение переменной не изменяется.
Пример: ${2 :? "Не хватает второго параметра"}

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

USERS='who | wd -l'
UP='date; uptime'
I='whoami'

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

grep 'id -un' /etc/passwd
 

Управляющие структуры.

К управляющим структурам относятся:
1. Конструкция if-fi
2. Конструкция case-esac

Общий синтаксис конструкции if-fi

if список1 then
   список2
elif список3 then
   список4
else
   список5
fi

Конструкция if-fi работает так же, как и в других языках программирования.Если список1 (условие) истинный, выполняется список2, иначе выполняется список3 и проверяется его истинность и т.д. Допускается неограниченная вложенность операторов if.
Список - это список команд. Разделителем команд служит символ <;>. Список обязательно должен заканчиваться точкой с запятой. Пример списка: ls; dir; cat file;
При программировании на bash есть один подводный камень, относящийся к логическим выражениям. В других языках программирования выражение <истина> обозначается как <true>, а <ложь> - как <false>. В языке C с выражением <ложь> сопоставляется нулевое значение переменной, а за истину принимается любое ненулеое значение. В bash все немного по-другому. За истину в конструкции if принимается 0, так как 0 - это код нормального завершения программы (команды). Правильнее конструкцию if трактовать так: если код завершения списка команд, задающего условие в конструкции if, равен 0, то будет выполнен список2. Код завершения последней команды можно узнать с помощью переменной $?.
Например,
if [ $? -ne 0 ]; then echo "Ошибка. См. файл протокола"; fi;
В этом примере мы проверяем код завершения последней команды. Если он не равен нулю (-ne), мы выводим сообщение об ошибке. Кроме опции -ne можно использовать такие опции:

  1. -eq - равно
  2. -lt - меньше
  3. -gt - больше
  4. -le - меньше или равно
  5. -ge - больше или равно
Сравнение строк:
  1. = - равно
  2. != - не равно

Символ "!" является символом логической операции NOT (отрицание). Кроме этого символа, можно использовать опции команды -o и -a, которые обозначают логические операции ИЛИ (OR) и И (AND).
Проверить существование файла можно опцией -e, а существование каталога - d.
Все эти опции являются параметрами программы test. Другими словами, вместо квадратных скобок вы можете использовать команду test, поэтому следующие выражения аналогичны

test -e /etc.passwd
[-e /etc/passwd]

Cинтаксис блока выбора (case - выбор):

case значение in
 шаблон1) список1 ;;
 ...
 шаблонN) списокN ;;
esac

Работает этот блок почти также, как в языке С. Однако есть небольшая разница: если найдено совпадение с каким-нибудь шаблоном (например, шаблонN-2) и выполнен соответствующий список команд списокN-2, осуществляется выход из блока. В языке С для достижения этого эффекта нужно было использовать оператор break, иначе выполнялись вы все списки после спискаN-2: списокN-1, списокN.
Вместо дейтсвия по умочанию нужно использовать шаблон *). Этот шаблон будет использоваться, когда не найдено совпадение ни с одним из шаблонов. Например,
case $A in
1) echo "A=1";;
2) echo "A=2";;
*) echo "A<>1 and A<>2";;
esac
 

Циклы.

Интерпретатор bash поддерживает циклы for, while, until, select, а интерпретатор sh только for и while.
В этой статье я рассмотрю только первые два цикла - for и while.

Синтаксис цикла for:

for имя_переменной in  список1
do
список2
done

Простой пример:

for i in 1 2 3 4 5; do echo $i; done

На экране вы увидите
1 2 3 4 5
Еще раз напомню, что любой список в bash должен заканчивать точкой с запятой.
Построчно вывести содержимое файла file.txt мы можем с помощью такого цикла

for str in 'cat ./file.txt'
do
echo "$str";
done

Цикл for закончит свою работу, когда будет обработан последний элемент списка, в данном случае, когда на экран будет выведена последняя строка файла file.txt

Синтаксис цикла while:

while список1
do
 список2
done

Цикл while будет выполняться, пока условие, заданное в списке список1, будет истинным. Поэтому цикл while иногда называют циклом с истинным условием. Например,

a=1
while [$a -lt 10]
do
 echo $a
 a = $(( $a + 1 ))
done

На экране вы увидите:
1 2 3 4 5 6 7 8 9
Когда переменная a примет значение 10, цикл завершит свою работу, так как программа test вернет значение false (a уже не меньше, а равен 10).
 

Подоболочки

Рассмотрим два почти аналогичных сценария

#!/bin/bash
# Сценарий 1
NUM="one";  (NUM="two"; echo $NUM;);
echo $NUM

#!/bin/bash
# Сценарий 2
NUM="one";  {NUM="two"; echo $NUM;};
echo $NUM

Сценарий 1 выведет на экран следующую информацию:

two
one

А сценарий 2

two
two

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

В этой статье я попытался объять необъятное, поэтому совсем не удивительно, что некоторые возможности bash (функции, циклы until и select, протоколирование, обработка сигналов:) не рассмотрены, а некоторые рассмотрены не очень подробно (команда test). Возможно, эти возможности я рассмотрю в своих последующих статьях. А сейчас могу предложить вам список ссылок, посвященных bash, shell, а также Unix-системам вообще: http://dkws.narod.ru/linux/books_.html



Вниманию вебмастеров: использование данной статьи возможно только в соответствии
с правилами использования материалов сайта <Софтерра> (http://www.softerra.ru/site/rules/)