«Бог не меняет того, что (происходит) с людьми, пока они сами не изменят своих помыслов.» Коран, Сура 12:13

Спец курс (Автоматизация процесса проектирования)/Лекция 1 (BASH)

Материал из Wiki
Перейти к: навигация, поиск

Содержание

Введение

Название BASH -- это аббревиатура от "Bourne-Again Shell" и игра слов от, ставшего уже классикой, "Bourne Shell" Стефена Бурна (Stephen Bourne). В последние годы BASH достиг такой популярности, что стал стандартной командной оболочкой de facto для многих разновидностей UNIX. Большинство принципов программирования на BASH одинаково хорошо применимы и в других командных оболочках, таких как Korn Shell (ksh), от которой Bash позаимствовал некоторые особенности, [2] и C Shell и его производных. (Примечательно, что C Shell не рекомендуется к использованию из-за отдельных проблем, отмеченных Томом Кристиансеном (Tom Christiansen) в октябре 1993 года на Usenet post

Первый скрипт

  • Создаем файл first_script.sh
  • Даем файлу права на запуск ( chmod 755 first_script.sh )
  • Содержимое файла
#!/bin/bash
echo "hello world"

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

В shell-скриптах последовательность #! должна стоять самой первой и задает интерпретатор (sh или bash). Интерпретатор, в свою очередь, воспринимает эту строку как комментарий, поскольку она начинается с символа #. Если в сценарии имеются еще такие же строки, то они воспринимаются как обычный комментарий.

#!/bin/bash

Оператор echo

Для вывода текстовых сообщений или инофрмации в поток вывода используется оператор echo

#!/bin/bash
 
echo "Имя скрипта — \$0"
echo "Первый аргумент: \$1"
echo "Второй аргумент: \${2}"
echo "Семнадцатый аргумент: \${17}"
echo "Количество аргументов: \$#"

Переменные

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

Бремя отслеживания типа той или иной переменной полностью лежит на плечах программиста. Bash не будет делать это за вас!

  • Для объявления переменной нужно указать ее имя и задать значение.
#!/bin/bash
# Присваивание значений переменным 
a=375
  • Чтобы использовать значение переменной используют специальный символ $
#!/bin/bash
# Присваивание значений переменным и подстановка значений переменных
a=375
hello=$a
  • Использование пробельных символов с обеих сторон символа "=" присваивания недопустимо.
    • Если записать "VARIABLE =value", то интерпретатор попытается выполнить команду "VARIABLE" с параметром "=value".
    • Если записать "VARIABLE= value", то интерпретатор попытается установить переменную окружения "VARIABLE" в "" и выполнить команду "value".
  • Примечательно, что написание $variable фактически является упрощенной формой написания ${variable}
#!/bin/bash
hello='hello'
echo hello    # Это не ссылка на переменную, выведет строку "hello".
echo $hello
echo ${hello} # Идентично предыдущей строке.
echo "$hello"
echo "${hello}"
  • Если в значениях переменных встречаются пробелы, то использование кавычек обязательно.
#!/bin/bash
numbers="один два три"
other_numbers="1 2 3"
echo "numbers = $numbers"
echo "other_numbers = $other_numbers"   # other_numbers = 1 2 3
  • Неинициализированная переменная хранит "пустое" значение - не ноль!. Объявление неинициализированной переменной (то же, что и присваивание пустого значения, см. выше).
#!/bin/bash
echo "uninitialized_variable = $uninitialized_variable"
uninitialized_variable=   
echo "uninitialized_variable = $uninitialized_variable"
uninitialized_variable=23     
echo "uninitialized_variable = $uninitialized_variable"
  • Использование неинициализированных переменных может приводить к ошибкам разного рода в процессе исполнения.
  • Оператор unset используется для того, чтобы инициализированную переменную сделать не инициализированной
#!/bin/bash
uninitialized_variable=23       # Присваивание.
unset uninitialized_variable    # Сброс.
echo "uninitialized_variable = $uninitialized_variable"
                                # Переменная содержит "пустое" значение.

Присваивание значений переменным

  • '=' оператор присваивания (пробельные символы до и после оператора -- недопустимы)
#!/bin/bash
a=879
echo "Значение переменной \"a\" -- $a."
  • Можно использовать присваивание совместно с оператором let
#!/bin/bash
let a=16+5
echo "Значение переменной \"a\" теперь стало равным: $a."
  • Неявное присваивание
#!/bin/bash
echo -n "Значения переменной \"a\" в цикле: "
for a in 7 8 9 11
do
  echo -n "$a "
done
  • Присваивание с оператором read
#!/bin/bash
echo -n "Введите значение переменной \"a\" "
read a
echo "Значение переменной \"a\" теперь стало равным: $a."
  • Присваивание переменным переменных
#!/bin/bash
a=23              # Простейший случай
echo $a
b=$a
echo $b
  • Маскированное присваивание переменных с использованием обратных ковычек `...`
#!/bin/bash
a=`echo Hello!`   # В переменную 'a' попадает результат работы команды 'echo'
echo $a
a=`ls -l`         # В переменную 'a' записывается результат работы команды 'ls -l'
echo $a           # Кавычки отсутствуют, удаляются лишние пробелы и пустые строки.
echo
echo "$a"         # Переменная в кавычках, все пробелы и пустые строки сохраняются.
  • Маскированное присваивание переменных с использованием $(...) (более современный метод, по сравнению с обратными кавычками)
#!/bin/bash
a=$(echo Hello\!)   # В переменную 'a' попадает результат работы команды 'echo'
echo $a

Переменные Bash не имеют типов

  • Тип переменной определяется из контекса
 
#!/bin/bash
a=2334                   # Целое число.
let "a += 1"
echo "a = $a "           # a = 2335
echo                     # Все еще целое число.
 
b=${a/23/BB}             # замена "23" на "BB".
                         # Происходит трансформация числа в строку.
echo "b = $b"            # b = BB35
declare -i b             # Явное указание типа здесь не поможет.
echo "b = $b"            # b = BB35
 
let "b += 1"             # BB35 + 1 =
echo "b = $b"            # b = 1
echo
 
c=BB34
echo "c = $c"            # c = BB34
d=${c/BB/23}             # замена "BB" на "23".
                         # Переменная $d становится целочисленной.
echo "d = $d"            # d = 2334
let "d += 1"             # 2334 + 1 =
echo "d = $d"            # d = 2335
  • Пустая переменная становиться целочисленной
 
#!/bin/bash
e=""
echo "e = $e"            # e =
let "e += 1"             # Арифметические операции допускают использование "пустых" переменных?
echo "e = $e"            # e = 1
  • Необъявленная переменная трансформируется в целочисленную.
#!/bin/bash
echo "f = $f"            # f =
let "f += 1"             # Арифметические операции допустимы?
echo "f = $f"            # f = 1

Использование ковычек в значения переменных

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

(Символ называется "специальным", если он несет дополнительную смысловую нагрузку, например символ шаблона -- *.)

  • Желательно использовать двойные кавычки (" ") при обращении к переменным. Это предотвратит интерпретацию специальных символов, которые могут содержаться в именах переменных, за исключением $, ` (обратная кавычка) и \ (escape -- обратный слэш).
  • То, что символ $ попал в разряд исключений, позволяет выполнять обращение к переменным внутри строк, ограниченных двойными кавычками ("$variable"), т.е. выполнять подстановку значений переменных.
  • Двойные кавычки могут быть использованы для предотвращения разбиения строки на слова. Заключение строки в кавычки приводит к тому, что она передается как один аргумент, даже если она содержит пробельные символы - разделители.
variable1="a variable containing five words"
COMMAND This is $variable1    # Исполнение COMMAND с 7 входными аргументами:
# "This" "is" "a" "variable" "containing" "five" "words"
 
COMMAND "This is $variable1"  # Исполнение COMMAND с одним входным аргументом:
# "This is a variable containing five words"
 
 
variable2=""    # Пустая переменная.
 
COMMAND $variable2 $variable2 $variable2        # Исполнение COMMAND без аргументов.
COMMAND "$variable2" "$variable2" "$variable2"  # Исполнение COMMAND с 3 "пустыми" аргументами.
COMMAND "$variable2 $variable2 $variable2"      # Исполнение COMMAND с 1 аргументом (и 2 пробелами).

Проверка условий

  • В Bash, для проверки условий, имеется команда test, различного вида скобочные операторы и условный оператор if/then.
  • Оператор if/then проверяет -- является ли код завершения списка команд 0 (поскольку 0 означает "успех"), и если это так, то выполняет одну, или более, команд, следующие за словом then.
  • Существует специальная команда -- [ (левая квадратная скобка). Она является синонимом команды test, и является встроенной командой (т.е. более эффективной, в смысле производительности). Эта команда воспринимает свои аргументы как выражение сравнения или как файловую проверку и возвращает код завершения в соответствии с результатами проверки (0 -- истина, 1 -- ложь).
#!/bin/bash
echo "Что есть истина"
 
echo "Проверяется \"1\""
if [ 1 ]      # единица
then
  echo "1 -- это истина."
else
  echo "1 -- это ложь."
fi            # 1 -- это ложь.
 
echo
 
echo "Testing \"-1\""
if [ -1 ]     # минус один
then
  echo "-1 -- это истина."
else
  echo "-1 -- это ложь."
fi            # -1 -- это истина.
 
echo
 
echo "Проверяется \"NULL\""
if [ ]        # NULL (пустое условие)
then
  echo "NULL -- это истина."
else
  echo "NULL -- это ложь."
fi            # NULL -- это ложь.
 
echo
 
echo "Проверяется \"xyz\""
if [ xyz ]    # строка
then
  echo "Случайная строка -- это истина."
else
  echo "Случайная строка -- это ложь."
fi            # Случайная строка -- это истина.
 
echo
 
echo "Проверяется \"\$xyz\""
if [ $xyz ]   # Проверка, если $xyz это null, но...
              # только для неинициализированных переменных.
then
  echo "Неинициализированная переменная -- это истина."
else
  echo "Неинициализированная переменная -- это ложь."
fi            # Неинициализированная переменная -- это ложь.
 
echo
 
echo "Проверяется \"-n \$xyz\""
if [ -n "$xyz" ]            # Более корректный вариант.
then
  echo "Неинициализированная переменная -- это истина."
else
  echo "Неинициализированная переменная -- это ложь."
fi            # Неинициализированная переменная -- это ложь.
 
echo
 
 
xyz=          # Инициализирована пустым значением.
 
echo "Проверяется \"-n \$xyz\""
if [ -n "$xyz" ]
then
  echo "Пустая переменная -- это истина."
else
  echo "Пустая переменная -- это ложь."
fi            # Пустая переменная -- это ложь.
  • Начиная с версии 2.02, Bash предоставляет в распоряжение программиста конструкцию ... расширенный вариант команды test, которая выполняет сравнение способом более знакомым программистам, пишущим на других языках программирования.
  • Обратите внимание: [[ -- это зарезервированное слово, а не команда. Bash исполняет $a -lt $b как один элемент, который имеет код возврата.
  • Круглые скобки (( ... )) и предложение let ... так же возвращают код 0, если результатом арифметического выражения является ненулевое значение. Таким образом, арифметические выражения могут участвовать в операциях сравнения.
  • Условный оператор if проверяет код завершения любой команды, а не только результат выражения, заключенного в квадратные скобки.
#!/bin/bash
if cmp a b &> /dev/null  # Подавление вывода.
then echo "Файлы a и b идентичны."
else echo "Файлы a и b имеют различия."
fi
 
if grep -q Bash file
then echo "Файл содержит, как минимум, одно слово Bash."
fi
 
if COMMAND_WHOSE_EXIT_STATUS_IS_0_UNLESS_ERROR_OCCURRED
then echo "Команда выполнена успешно."
else echo "Обнаружена ошибка при выполнении команды."
fi
  • Оператор if/then допускает наличие вложенных проверок.
#!/bin/bash
 
if echo "Следующий *if* находится внутри первого *if*."
  if [[ $comparison = "integer" ]]
    then (( a < b ))
  else
    [[ $a < $b ]]
  fi
then
  echo '$a меньше $b'
fi
  • Когда if и then располагаются в одной строке, то конструкция if должна завершаться точкой с запятой. И if, и then -- это зарезервированные слова. Зарезервированные слова начинают инструкцию, которая должна быть завершена прежде, чем в той же строке появится новая инструкция.
if [ -x "$filename" ]; then
  • elif -- это краткая форма записи конструкции else if. Применяется для построения многоярусных инструкций if/then.
#!/bin/bash
if [ condition1 ]
then
   command1
   command2
   command3
elif [ condition2 ]
# То же самое, что и else if
then
   command4
   command5
else
   default-command
fi
  • Команда test -- это встроенная команда Bash, которая выполняет проверки файлов и производит сравнение строк. Таким образом, в Bash-скриптах, команда test не вызывает внешнюю (/usr/bin/test) утилиту, которая является частью пакета sh-utils. Аналогично, [ не производит вызов утилиты /usr/bin/[, которая является символической ссылкой на /usr/bin/test.
bash$ type test
test is a shell builtin
bash$ type '['
[ is a shell builtin
bash$ type '[['
[[ is a shell keyword
bash$ type ']]'
]] is a shell keyword
bash$ type ']'
bash: type: ]: not found
  • Эквиваленты команды test -- /usr/bin/test, [ ], и /usr/bin/[
#!/bin/bash
if test -z "$1"
then
  echo "Аргументы командной строки отсутствуют."
else
  echo "Первый аргумент командной строки: $1."
fi
  • Конструкция [[ ]] более универсальна, по сравнению с [ ]. Этот расширенный вариант команды test перекочевал в Bash из ksh88. Внутри этой конструкции не производится никакой дополнительной интерпретации имен файлов и не производится разбиение аргументов на отдельные слова, но допускается подстановка параметров и команд. Конструкция ... более предпочтительна, нежели [ ... ], поскольку поможет избежать некоторых логических ошибок. Например, операторы &&, ||, < и > внутри [[ ]] вполне допустимы, в то время как внутри [ ] порождают сообщения об ошибках.
#!/bin/bash
file=/etc/passwd
if [[ -e $file ]]
then
  echo "Файл паролей найден."
fi
  • Строго говоря, после оператора if, ни команда test, ни квадратные скобки ( [ ] или [[ ]] ) не являются обязательными. Инструкция "if COMMAND" возвращает код возврата команды COMMAND. Точно так же, условие, находящееся внутри квадратных скобок может быть проверено без использования оператора if.
dir=/home/bozo
if cd "$dir" 2>/dev/null; then   # "2>/dev/null" подавление вывода сообщений об ошибках.
  echo "Переход в каталог $dir выполнен."
else
  echo "Невозможно перейти в каталог $dir."
fi
 
var1=20
var2=22
[ "$var1" -ne "$var2" ] && echo "$var1 не равно $var2"
home=/home/bozo
[ -d "$home" ] || echo "каталог $home не найден."
  • Внутри (( )) производится вычисление арифметического выражения. Если результатом вычислений является ноль, то возвращается 1, или "ложь". Ненулевой результат дает код возврата 0, или "истина". То есть полная противоположность инструкциям test и [ ], обсуждавшимся выше.
#!/bin/bash
# Проверка арифметических выражений.
 
# Инструкция (( ... )) вычисляет арифметические выражения.
# Код возврата противоположен коду возврата инструкции [ ... ] !
 
(( 0 ))
echo "Код возврата \"(( 0 ))\":  $?."         # 1
 
(( 1 ))
echo "Код возврата \"(( 1 ))\":  $?."         # 0
 
(( 5 > 4 ))                                   # true
echo "Код возврата \"(( 5 > 4 ))\":  $?."     # 0
 
(( 5 > 9 ))                                   # false
echo "Код возврата \"(( 5 > 9 ))\":  $?."     # 1
 
(( 5 - 5 ))                                   # 0
echo "Код возврата \"(( 5 - 5 ))\":  $?."     # 1
 
(( 5 / 4 ))                                   # Деление, все в порядке
echo "Код возврата \"(( 5 / 4 ))\":  $?."     # 0
 
(( 1 / 2 ))                                   # Результат деления < 1.
echo "Код возврата \"(( 1 / 2 ))\":  $?."     # Округляется до 0.
                                              # 1
 
(( 1 / 0 )) 2>/dev/null                       # Деление на 0.
echo "Код возврата \"(( 1 / 0 ))\":  $?."     # 1
 
# Для чего нужна инструкция "2>/dev/null" ?
# Что произойдет, если ее убрать?
# Попробуйте убрать ее и выполнить сценарий.
 
exit 0

Операции проверки файлов

Входные параметры

Код завершения и возвращаемое значение

  • Команда exit может использоваться для завершения работы сценария, точно так же как и в программах на языке C. Кроме того, она может возвращать некоторое значение, которое может быть проанализировано вызывающим процессом.
  • В соответствии с соглашениями, 'exit 0' указывает на успешное завершение, в то время как ненулевое значение означает ошибку.
#!/bin/bash
 
echo hello
echo $?    # код возврата = 0, поскольку команда выполнилась успешно.
 
lskdf      # Несуществующая команда.
echo $?    # Ненулевой код возврата, поскольку команду выполнить не удалось.
 
echo
 
exit 113   # Явное указание кода возврата 113.
           # Проверить можно, если набрать в командной строке "echo $?"
           # после выполнения этого примера.