Спец курс (Автоматизация процесса проектирования)/Лекция 1 (BASH)
Введение
Название 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 $?" # после выполнения этого примера.