22 January 2021
Программы-оболочки.
Интерпретатор bash
Командный язык bash
В той статье мы поговорим о неотъемленной части пользовательского
интерфейса 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, которая стандартна для
большинства систем. Однако сам командный язык подробно разобран не будет
из-за его объемности. Итак, начнем по порядку с регистрации пользователя.
Как я уже отмечал, после успешной аутентификации пользователя, запускается
программа-оболочка (в нашем случае это /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
Командный язык 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 использует такие метасимволы, имеющее для него особое
значение:
* ? ; & ( ) | ^ < > <возврат_каретки> <табуляция>
<пробел>
Для того, чтобы использовать эти символы как они есть, нужно их цитировать
с помощью символа \. Например, символ перевода строки можно цитировать
так \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 | Домашний каталог |
Имя файла, в который поступает электронная почта | |
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
можно использовать такие опции:
Сравнение строк:
Символ ! является символом логической операции 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-системам вообще: https://dkws.narod.ru/linux/books_.html