В той статье мы поговорим о неотъемлемой части пользовательского интерфейса Linux – о программах-оболочках. Именно программа-оболочка запускается после регистрации пользователя в системе. Программы-оболочки часто называют командными интерпретаторами, потому что они обрабатывают (интерпретируют) команды, введенные пользователем. Во времена DOS этим занимался файл C:command.com. В файле /etc/passwd для каждого пользователя указывается, какую оболочку он будет использовать.
Из листинга видно, что оба пользователя используют оболочку /bin/bash. Оболочка, как правило, указывается в последнем поле файла passwd. Список всех установленных в системе программ-оболочек содержится в файле /etc/shells. У меня он выглядит так:
На любой 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).
Запустите сценарий ./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 Специальные переменные Переменная Значение HOME Домашний каталог MAIL Имя файла, в который поступает электронная почта LOGNAME Имя пользователя, которое использовалось для входа в систему PATH Путь вызова SHELL Имя интерпретатора команд PWD Текущий каталог UID Идентификатор пользователя, запустившего сценарий RANDOM Случайное число в диапазоне от 0 до 32767 SECONDS Число секунд, прошедшее с момента запуска оболочки
Кроме этих переменных устанавливаются и другие переменные, назначение которых вы можете узнать в документации по bash. В таблице 2 представлены переменные, которые используются для обозначения параметров командной строки.
Таблица 2. Переменная Значение $0 Имя выполняемой команды. Для сценария – путь, указанный при его вызове. $1 Первый параметр, указанный при вызове сценария. Аналогично, $2 – второй, $n – n-ый параметр. $# Число параметров, которые были указаны при вызове сценария. $* Все параметры, заключенные в кавычки: “$1 $2 ...” $? Код завершения последней команды $$ Номер текущего процесса (PID)
Арифметические выражения
Подстановка арифметических выражений осуществляется с помощью конструкции
$(( выражение ))
Например,
N = $(( (10+5)/2 )) echo $N
На экране вы увидите 3, а не 3,5, потому что интерпретатор bash использует целочисленные вычисления. Количество часов, прошедшее с момента запуски оболочки можно вычислить так: hrs = $(( $SECONDS/3600 ))
Подстановка переменных. Подстановка значений.
Интерпретатор bash предоставляет нам довольно гибкий механизм подстановки переменных. При этом переменная будет использоваться не всегда, а в зависимости от определенных обстоятельств. (см. таблицу 3).
Таблица 3. ${переменная:=значение} Значение присваивается переменной, если она не определена или является пустой строкой. ${переменная:?сообщение} Если переменная не определена или является пустой строкой, выводится сообщение ${переменная:+значение} Если переменная инициализирована (определена), вместо нее используется указанное в конструкции значение. (*) ${переменная} Если переменная определена, подставляется ее значение. Скобки используются лишь для того, если после переменной стоит символ, который может «приклеиться» к имени переменной. ${переменная:-значение} Если переменная определена и не является пустой строкой, подставляется ее значение, иначе подставляется значение, указанное в конструкции. (*)
(*) Реальное значение переменной не изменяется. Пример: ${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 можно использовать такие опции:
-eq – равно -lt – меньше -gt – больше -le – меньше или равно -ge – больше или равно Сравнение строк: = – равно != – не равно Символ "!" является символом логической операции 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).
Если список команд заключен в фигурные скобки, то он выполняется в текущей оболочке, а если в круглые – то в подоболочке. Механизм подоболочек, как вы уже успели догадаться, связан с областью действия переменных. Как и в любом другом языке программирования, в bash существуют два типа переменных: глобальный и локальные. Поскольку переменные в bash не описываются в начале программы, как в других языках, например, Pascal, внутри блока можно использовать как глобальные переменные (описанные в основном блоке сценария), так и локальные (описанные в этом блоке, при условии, что этот блок является подоболочкой).