|
|
# Шпаргалка по Bash скриптам
|
|
|
|
|
|
<div align="right"><a href="https://github.com/cheatsnake/backend-cheats#%D1%81%D0%BA%D1%80%D0%B8%D0%BF%D1%82%D1%8B-bash">Вернуться на главную страницу ⬆️</a></div>
|
|
|
|
|
|
**Содержание:**
|
|
|
|
|
|
- [Hello world](#hello-world)
|
|
|
- [Комментарии](#комментарии)
|
|
|
- [Переменные](#переменные)
|
|
|
- [Пользовательский ввод](#пользовательский-ввод)
|
|
|
- [Передача аргументов](#передача-аргументов)
|
|
|
- [Условия if else](#условия-if-else)
|
|
|
- [Операторы условий](#операторы-условий)
|
|
|
- [Логические операторы](#логические-операторы)
|
|
|
- [Арифметические операторы](#арифметические-операторы)
|
|
|
- [Конструкция switch case](#конструкция-switch-case)
|
|
|
- [Массивы](#массивы)
|
|
|
- [Цикл while](#цикл-while)
|
|
|
- [Цикл until](#цикл-until)
|
|
|
- [Цикл for](#цикл-for)
|
|
|
- [Цикл select](#цикл-select)
|
|
|
- [Break и continue в циклах](#break-и-continue-в-циклах)
|
|
|
- [Функции](#функции)
|
|
|
- [Локальные переменные](#локальные-переменные)
|
|
|
- [Ключевое слово readonly](#ключевое-слово-readonly)
|
|
|
- [Обработка сигналов](#обработка-сигналов)
|
|
|
|
|
|
Скрипты Bash имеют расширение `.sh`:
|
|
|
```
|
|
|
$ touch script.sh
|
|
|
```
|
|
|
|
|
|
Хорошей практикой считается указывать путь до вашего терминала вначале каждого скрипта:
|
|
|
```sh
|
|
|
#! /bin/bash
|
|
|
```
|
|
|
> Этот прием называется **shebang**, подробнее можно почитать [тут](https://ru.wikipedia.org/wiki/%D0%A8%D0%B5%D0%B1%D0%B0%D0%BD%D0%B3_(Unix))
|
|
|
|
|
|
Список доступных терминалов в вашей системе можно посмотреть с помощью этой команды:
|
|
|
```
|
|
|
$ cat /etc/shells
|
|
|
```
|
|
|
|
|
|
## Hello world
|
|
|
|
|
|
```sh
|
|
|
#! /bin/bash
|
|
|
echo "Hello world"
|
|
|
```
|
|
|
|
|
|
Запуск скрипта:
|
|
|
```
|
|
|
$ bash script.sh
|
|
|
```
|
|
|
|
|
|
Скрипт можно сделать исполняемым файлом и запускать без команды `bash`:
|
|
|
```
|
|
|
$ chmod +x script.sh
|
|
|
```
|
|
|
```
|
|
|
$ ./script.sh
|
|
|
```
|
|
|
|
|
|
## Комментарии
|
|
|
|
|
|
Однострочные комментарии:
|
|
|
```sh
|
|
|
# Это просто коммент
|
|
|
# И это тоже
|
|
|
echo "Hello from bash" # эта команда выводит строку в консоль
|
|
|
```
|
|
|
|
|
|
Мультистрочные комментарии:
|
|
|
```sh
|
|
|
: 'Мультистрочные комментарии очень удобны
|
|
|
для подробного описания ваших скриптов.
|
|
|
Успользуйте их с умом!'
|
|
|
```
|
|
|
|
|
|
## Переменные
|
|
|
|
|
|
```sh
|
|
|
MY_STRING="bash is cool"
|
|
|
echo $MY_STRING # Вывод значения переменной
|
|
|
```
|
|
|
> Имя переменной не должно начинаться с цифры
|
|
|
|
|
|
## Пользовательский ввод
|
|
|
|
|
|
Команда `read` читает пользовательский ввод и записывает его в указанную переменную:
|
|
|
```sh
|
|
|
echo "Введите ваше имя:"
|
|
|
read NAME
|
|
|
echo "Привет $NAME!"
|
|
|
```
|
|
|
> Если переменная не указана, то команда `read` по умолчанию сохранит все данные в переменную `REPLY`
|
|
|
|
|
|
|
|
|
Можно записывать несколько переменных. Для этого, при вводе из терминала, значения необходимо разделять пробелом:
|
|
|
```sh
|
|
|
read V1 V2 V3
|
|
|
echo "1 переменная: $V1"
|
|
|
echo "2 переменная: $V2"
|
|
|
echo "3 переменная: $V3"
|
|
|
```
|
|
|
```
|
|
|
$ bash script.sh
|
|
|
$ hello world some other text
|
|
|
1 переменная: hello
|
|
|
2 переменная: world
|
|
|
3 переменная: some other text
|
|
|
```
|
|
|
|
|
|
Флаг `-a` позволяет создать массив в который будут записываться строки пользовательского ввода разделенные пробелом:
|
|
|
```sh
|
|
|
read -a NAMES
|
|
|
echo "Массив имён: ${NAMES[0]}, ${NAMES[1]}, ${NAMES[2]}"
|
|
|
```
|
|
|
```
|
|
|
$ bash script.sh
|
|
|
Alex Mike John
|
|
|
Массив имён: Alex, Mike, John
|
|
|
```
|
|
|
|
|
|
Флаг `-p` позволяет не переносить пользовательский ввод на следующую строку.
|
|
|
|
|
|
Флаг `-s` позволяет скрыть вводимые символы (как это происходит при вводе пароля).
|
|
|
```sh
|
|
|
read -p "Введите ваш логин: " LOGIN
|
|
|
read -sp "Введите ваш пароль: " PASSWD
|
|
|
```
|
|
|
```
|
|
|
$ bash script.sh
|
|
|
Введите ваш логин: bash_hacker
|
|
|
Введите ваш пароль:
|
|
|
```
|
|
|
|
|
|
## Передача аргументов
|
|
|
|
|
|
Аргументы это просто значения, которые могут быть указаны при запуске скрипта.
|
|
|
|
|
|
Всем переданным аргументам присваивается уникальное имя равное их порядковому номеру:
|
|
|
```sh
|
|
|
echo "Аргумент 1 - $1; aргумент 1 - $2; aргумент 1 - $3."
|
|
|
```
|
|
|
```
|
|
|
$ bash script.sh hello test 1337
|
|
|
Аргумент 1 - hello; aргумент 1 - test; aргумент 1 - 1337.
|
|
|
```
|
|
|
|
|
|
Нулевой аргумент всегда равен названию файла со скриптом:
|
|
|
```sh
|
|
|
echo "Вы запустили файл $0"
|
|
|
```
|
|
|
```
|
|
|
$ bash script.sh
|
|
|
Вы запустили файл script.sh
|
|
|
```
|
|
|
|
|
|
Все аргументы можно положить в именованный массив:
|
|
|
```sh
|
|
|
args=("$@")
|
|
|
echo "Полученные аргументы: ${args[0]}, ${args[1]}, ${args[2]}."
|
|
|
```
|
|
|
```
|
|
|
$ bash script.sh some values 123
|
|
|
Полученные аргументы: some, values, 123
|
|
|
```
|
|
|
> `@` - это название массива по умолчанию, который хранит все аргументы (за исключением нулевого)
|
|
|
|
|
|
Количество переданных аргументов (за исключением нулевого) хранится в переменной `#`:
|
|
|
```sh
|
|
|
echo "Всего получено аргументов: $#"
|
|
|
```
|
|
|
|
|
|
## Условия if else
|
|
|
|
|
|
Условия всегда начинаются с ключевого слова `if` и заканчиваются на `fi`:
|
|
|
```sh
|
|
|
echo "Введите ваш возраст:"
|
|
|
read AGE
|
|
|
|
|
|
if (($AGE >= 18))
|
|
|
then
|
|
|
echo "Доступ разрешен"
|
|
|
else
|
|
|
echo "Доступ запрещен"
|
|
|
fi
|
|
|
```
|
|
|
```
|
|
|
$ bash script.sh
|
|
|
Введите ваш возраст:
|
|
|
19
|
|
|
Доступ разрешен
|
|
|
|
|
|
$ bash script.sh
|
|
|
Введите ваш возраст:
|
|
|
16
|
|
|
Доступ запрещен
|
|
|
```
|
|
|
|
|
|
Условий может быть сколько угодно много, для этого используется конструкция `elif`, которая также как `if` может проверять условия:
|
|
|
```sh
|
|
|
read COMMAND
|
|
|
|
|
|
if [ $COMMAND = "help" ]
|
|
|
then
|
|
|
echo "Доступные команды:"
|
|
|
echo "ping - вернет строку PONG"
|
|
|
echo "version - вернет номер версии программы"
|
|
|
elif [ $COMMAND = "ping" ]
|
|
|
then
|
|
|
echo "PONG"
|
|
|
elif [ $COMMAND = "version" ]
|
|
|
then
|
|
|
echo "v1.0.0"
|
|
|
else
|
|
|
echo "Команда не определена. Воспользуйтесь командой 'help' для справки"
|
|
|
fi
|
|
|
```
|
|
|
> Обратите внимание, что после конструкций `if` и `elif` всегда следует строчка с ключевым словом `then` <br>
|
|
|
> Так же не забывайте отделять условия пробелами внутри фигурных скобок -> `[ condition ]`
|
|
|
|
|
|
## Операторы условий
|
|
|
|
|
|
Для цифр и строк могут использоваться разные операторы сравнения. Полные их списки с примерами приведены в таблицах ниже.
|
|
|
> Обратите внимания, что разные операторы используются с определенными скобками
|
|
|
|
|
|
### Операторы сравнения для чисел
|
|
|
|
|
|
| Оператор | Описание | Пример |
|
|
|
| -------- | -------------------- | ------------------ |
|
|
|
| -eq | равняется ли | if [ $age -eq 18 ] |
|
|
|
| -ne | не равняется | if [ $age -ne 18 ] |
|
|
|
| -gt | больше чем | if [ $age -gt 18 ] |
|
|
|
| -ge | больше чем или равно | if [ $age -ge 18 ] |
|
|
|
| -lt | меньше чем | if [ $age -lt 18 ] |
|
|
|
| -le | меньше чем или равно | if [ $age -le 18 ] |
|
|
|
| > | больше чем | if (($age > 18)) |
|
|
|
| < | меньше чем | if (($age < 18)) |
|
|
|
| => | больше чем или равно | if (($age => 18)) |
|
|
|
| <= | меньше чем или равно | if (($age <= 18)) |
|
|
|
|
|
|
### Операторы сравнения для строк
|
|
|
|
|
|
| Оператор | Описание | Пример |
|
|
|
| -------- | ------------------------------------------- | ------------------------ |
|
|
|
| = | проверка на равенство | if [ $str = "hello" ] |
|
|
|
| == | проверка на равенство | if [ $str == "hello" ] |
|
|
|
| != | проверка на НЕ равенство | if [ $str != "hello" ] |
|
|
|
| < | сравнение меньше чем по ASCII коду символов | if [[ $str < "hello" ]] |
|
|
|
| > | сравнение больше чем по ASCII коду символов | if [[ $str > "hello" ]] |
|
|
|
| -z | проверка пустая ли строка | if [ -z $str ] |
|
|
|
| -n | проверка есть ли в строке хоть один символ | if [ -n $str ] |
|
|
|
|
|
|
Так же существуют операторы для проверки различных условий над файлами.
|
|
|
|
|
|
### Операторы для проверки файлов
|
|
|
|
|
|
| Оператор | Описание | Пример |
|
|
|
| -------- | --------------------------------------------------------------------------------- | --------------- |
|
|
|
| -e | проверяет, существует ли файл | if [ -e $file ] |
|
|
|
| -s | проверяет, пустой ли файл | if [ -s $file ] |
|
|
|
| -f | проверяет, является ли файл обычным файлом, а не каталогом или специальным файлом | if [ -f $file ] |
|
|
|
| -d | проверяет, является ли файл каталогом | if [ -d $file ] |
|
|
|
| -r | проверяет, доступен ли файл для чтения | if [ -r $file ] |
|
|
|
| -w | проверяет, доступен ли файл для записи | if [ -w $file ] |
|
|
|
| -x | проверяет, является ли файл исполяемым | if [ -x $file ] |
|
|
|
|
|
|
## Логические операторы
|
|
|
|
|
|
Условия с оператором "И" возвращают истину только в том случае, когда все условия истины.
|
|
|
> Существует несколько вариантов написания условий с логическими операторами
|
|
|
|
|
|
```sh
|
|
|
if [ $age -ge 18 ] && [ $age -le ]
|
|
|
```
|
|
|
```sh
|
|
|
if [ $age -ge 18 -a $age -le ]
|
|
|
```
|
|
|
```sh
|
|
|
if [[ $age -ge 18 && $age -le ]]
|
|
|
```
|
|
|
|
|
|
Условия с оператором "ИЛИ" возвращают истину в том случае, когда хотя бы одно условие истинно.
|
|
|
|
|
|
```sh
|
|
|
if [ -r $file ] || [ -w $file ]
|
|
|
```
|
|
|
```sh
|
|
|
if [ -r $file -o -w $file ]
|
|
|
```
|
|
|
```sh
|
|
|
if [[ -r $file || -w $file ]]
|
|
|
```
|
|
|
|
|
|
## Арифметические операторы
|
|
|
|
|
|
```bash
|
|
|
num1=10
|
|
|
num2=5
|
|
|
|
|
|
# Сложение
|
|
|
echo $((num1 + num2)) # 15
|
|
|
echo $(expr $num1 + $num2) # 15
|
|
|
|
|
|
# Вычитание
|
|
|
echo $((num1 - num2)) # 5
|
|
|
echo $(expr $num1 - $num2) # 5
|
|
|
|
|
|
# Умножение
|
|
|
echo $((num1 * num2)) # 50
|
|
|
echo $(expr $num1 \* $num2) # 50
|
|
|
|
|
|
# Деление
|
|
|
echo $((num1 / num2)) # 2
|
|
|
echo $(expr $num1 / $num2) # 2
|
|
|
|
|
|
# Остаток от деления
|
|
|
echo $((num1 % num2)) # 0
|
|
|
echo $(expr $num1 % $num2) # 0
|
|
|
```
|
|
|
> Обратите внимание, что при использовании умножения с ключевым словом `expr` необходимо использовать косую черту.
|
|
|
|
|
|
## Конструкция switch case
|
|
|
|
|
|
Не всегда удобно использовать конструкции if/elif для большого количества условий. Для этого лучше подойдет конструкция case:
|
|
|
|
|
|
```sh
|
|
|
read COMMAND
|
|
|
|
|
|
case $COMMAND in
|
|
|
"/help" )
|
|
|
echo "Вы открыли справочное меню" ;;
|
|
|
"/ping" )
|
|
|
echo "PONG" ;;
|
|
|
"/version" )
|
|
|
echo "Текущая версия: 1.0.0" ;;
|
|
|
* )
|
|
|
echo "Такой команды нет :(" ;;
|
|
|
esac
|
|
|
```
|
|
|
|
|
|
> Случай со звездочкой * отработает лишь в том случае, если не подойдет ни одно из условий выше.
|
|
|
|
|
|
## Массивы
|
|
|
|
|
|
Массивы позволяют хранить целую коллекцию данных в одной переменной. С этой переменной можно удобно и легко взаимодействовать:
|
|
|
|
|
|
```sh
|
|
|
array=('aaa' 'bbb' 'ccc' 'ddd')
|
|
|
|
|
|
echo "Элементы массива: ${array[@]}"
|
|
|
echo "Первый элемент массива: ${array[0]}"
|
|
|
echo "Индексы элементов массива: ${!array[@]}"
|
|
|
|
|
|
array_length=${#array[@]}
|
|
|
echo "Длинна массива: ${array_length}"
|
|
|
echo "Последний элемент массива: ${array[$((array_length - 1))]}"
|
|
|
```
|
|
|
```
|
|
|
$ bash script.sh
|
|
|
Элементы массива: aaa bbb ccc ddd
|
|
|
Первый элемент массива: aaa
|
|
|
Индексы элементов массива: 0 1 2 3
|
|
|
Длинна массива: 4
|
|
|
Последний элемент массива: ddd
|
|
|
```
|
|
|
> Обратите внимание, что элементы массива разделются пробелом без запятой.
|
|
|
|
|
|
Элементы массива можно добавлять/перезаписывать/удалять по ходу выполнения скрипта:
|
|
|
|
|
|
```sh
|
|
|
array=('a' 'b' 'c')
|
|
|
|
|
|
array[3]='d'
|
|
|
echo ${array[@]} # a b c d
|
|
|
|
|
|
array[0]='x'
|
|
|
echo ${array[@]} # x b c d
|
|
|
|
|
|
array[0]='x'
|
|
|
echo ${array[@]} # x b c d
|
|
|
|
|
|
unset array[2]
|
|
|
echo ${array[@]} # x b d
|
|
|
```
|
|
|
|
|
|
## Цикл while
|
|
|
|
|
|
Цикл while повторяет выполение блока кода описанного между ключевыми словами `do` - `done` пока истино заданное условие.
|
|
|
|
|
|
```sh
|
|
|
i=0
|
|
|
|
|
|
while (( $i < 5 ))
|
|
|
do
|
|
|
i=$((i + 1))
|
|
|
echo "Итерация номер $i"
|
|
|
done
|
|
|
```
|
|
|
```
|
|
|
$ bash script.sh
|
|
|
Итерация номер 1
|
|
|
Итерация номер 2
|
|
|
Итерация номер 3
|
|
|
Итерация номер 4
|
|
|
Итерация номер 5
|
|
|
```
|
|
|
|
|
|
Операция увеличения числа на 1 единицу называется инкриментом и для неё существует специальная запись:
|
|
|
|
|
|
```sh
|
|
|
(( i++ )) # post increment
|
|
|
```
|
|
|
```sh
|
|
|
(( ++i )) # pre increment
|
|
|
```
|
|
|
|
|
|
Противоположная операция - декремент:
|
|
|
|
|
|
```sh
|
|
|
(( i-- )) # post decrement
|
|
|
```
|
|
|
```sh
|
|
|
(( --i )) # pre decrement
|
|
|
```
|
|
|
|
|
|
С помощью while циклов можно построчно читать различные файлы. Существует несколько способов сделать это:
|
|
|
|
|
|
```sh
|
|
|
echo "Чтение файла по строкам:"
|
|
|
while read line
|
|
|
do
|
|
|
echo $line
|
|
|
done < text.txt
|
|
|
```
|
|
|
|
|
|
```sh
|
|
|
echo "Чтение файла по строкам:"
|
|
|
cat text.txt | while read line
|
|
|
do
|
|
|
echo $line
|
|
|
done
|
|
|
```
|
|
|
|
|
|
```sh
|
|
|
echo "Чтение файла по строкам:"
|
|
|
while IFS='' read -r line
|
|
|
do
|
|
|
echo $line
|
|
|
done < text.txt
|
|
|
```
|
|
|
|
|
|
## Цикл until
|
|
|
|
|
|
Цикл until противоположен циклу while тем, что он выполняет блок кода описанный между ключевыми словами `do` - `done` тогда, когда заданное условие возвращает false:
|
|
|
|
|
|
```sh
|
|
|
i=5
|
|
|
until (( $i == 0 )) # будет выполняться пока i не станет равным 0
|
|
|
do
|
|
|
echo "Значение переменной i = $i"
|
|
|
(( i-- ))
|
|
|
done
|
|
|
```
|
|
|
```
|
|
|
$ bash script.sh
|
|
|
Значение переменной i = 5
|
|
|
Значение переменной i = 4
|
|
|
Значение переменной i = 3
|
|
|
Значение переменной i = 2
|
|
|
Значение переменной i = 1
|
|
|
```
|
|
|
|
|
|
## Цикл for
|
|
|
|
|
|
Самый классический цикл:
|
|
|
```sh
|
|
|
for (( i=1; i<=10; i++ ))
|
|
|
do
|
|
|
echo $i
|
|
|
done
|
|
|
```
|
|
|
|
|
|
В новых версиях Bash существует более удобный способ записи с помощью оператора `in`:
|
|
|
|
|
|
```sh
|
|
|
for i in {1..10}
|
|
|
do
|
|
|
echo $i
|
|
|
done
|
|
|
```
|
|
|
|
|
|
Условие после ключевого слова `in` в общем случае выгядит так:
|
|
|
|
|
|
```
|
|
|
{START..END..INCREMENT}
|
|
|
```
|
|
|
> _START_ - с какого элемента начинать цикл; <br>
|
|
|
> _END_ - до какого элемента продолжать цикл; <br>
|
|
|
> _INCREMENT_ - на сколько увеличивать элемент после каждой итерации (по умолчанию на 1).
|
|
|
|
|
|
|
|
|
Цикл for можно использовать для последовательного запуска набора команд:
|
|
|
|
|
|
```sh
|
|
|
for command in ls pwd date # Список команд для запуска
|
|
|
do
|
|
|
echo "---Запуск команды $command---"
|
|
|
$command
|
|
|
echo "------------------------"
|
|
|
done
|
|
|
```
|
|
|
```
|
|
|
$ bash script.sh
|
|
|
---Запуск команды ls---
|
|
|
script.sh text.txt
|
|
|
------------------------
|
|
|
---Запуск команды pwd---
|
|
|
/home/user/bash
|
|
|
------------------------
|
|
|
---Запуск команды date---
|
|
|
Сб 03 сен 2022 10:35:57 +03
|
|
|
------------------------
|
|
|
```
|
|
|
|
|
|
Ключевое слово `break` останавливает выполнение цикла.
|
|
|
|
|
|
Ключевое слово `continue` завершает текущую итерацию цикла и переходит к следующей. <br>
|
|
|
|
|
|
## Цикл select
|
|
|
|
|
|
Крайне удобный цикл для создания меню выбора опций:
|
|
|
|
|
|
```sh
|
|
|
select color in "Красный" "Зеленый" "Синий" "Белый"
|
|
|
do
|
|
|
echo "Вы выбрали $color цвет..."
|
|
|
done
|
|
|
```
|
|
|
```
|
|
|
$ bash script.sh
|
|
|
1) Красный
|
|
|
2) Зеленый
|
|
|
3) Синий
|
|
|
4) Белый
|
|
|
#? 1
|
|
|
Вы выбрали Красный цвет...
|
|
|
#? 2
|
|
|
Вы выбрали Зеленый цвет...
|
|
|
#? 3
|
|
|
Вы выбрали Синий цвет...
|
|
|
#? 4
|
|
|
Вы выбрали Белый цвет...
|
|
|
```
|
|
|
|
|
|
Цикл `select` очень хорошо сочетается с оператором выбора `case`. Таким образом можно очень просто создавать интерактивные консольные приложения с большим количеством разветвлений:
|
|
|
|
|
|
```sh
|
|
|
echo "---Добро пожаловать в меню---"
|
|
|
|
|
|
select cmd in "Запуск" "Настройки" "О программе" "Выход"
|
|
|
do
|
|
|
case $cmd in
|
|
|
"Запуск")
|
|
|
echo "Программа запущена"
|
|
|
echo "Введите число:"
|
|
|
read input
|
|
|
echo "$input в квадрате = $(( input * input ))" ;;
|
|
|
"Настройки")
|
|
|
echo "Настройки программы" ;;
|
|
|
"О программе")
|
|
|
echo "Версия 1.0.0" ;;
|
|
|
"Выход")
|
|
|
echo "Выход из программы..."
|
|
|
break ;;
|
|
|
esac
|
|
|
done
|
|
|
```
|
|
|
|
|
|
## Break и continue в циклах
|
|
|
|
|
|
Для принудительного выхода из цикла используется ключевое слово `break`:
|
|
|
|
|
|
```sh
|
|
|
count=1
|
|
|
|
|
|
while (($count)) # всегда возвращает истину
|
|
|
do
|
|
|
if (($count > 10))
|
|
|
then
|
|
|
break # принудительный выход несмотря на условие после while
|
|
|
else
|
|
|
echo $count
|
|
|
((count++))
|
|
|
fi
|
|
|
done
|
|
|
```
|
|
|
|
|
|
Для того, чтобы пропустить выполнение текущей итерации в цикле и перейти к следующей - используется ключевое слово `continue`:
|
|
|
|
|
|
```sh
|
|
|
for (( i=5; i>0; i-- ))
|
|
|
do
|
|
|
if ((i % 2 == 0))
|
|
|
then
|
|
|
continue
|
|
|
fi
|
|
|
|
|
|
echo $i
|
|
|
done
|
|
|
```
|
|
|
```
|
|
|
$ bash script.sh
|
|
|
5
|
|
|
3
|
|
|
1
|
|
|
```
|
|
|
|
|
|
## Функции
|
|
|
|
|
|
Функции - это именованные участки кода, которые могут переиспользоваться неограниченное количество раз:
|
|
|
|
|
|
```sh
|
|
|
hello() {
|
|
|
echo "Hello World!"
|
|
|
}
|
|
|
|
|
|
# вызываем функцию 3 раза:
|
|
|
hello
|
|
|
hello
|
|
|
hello
|
|
|
```
|
|
|
```
|
|
|
$ bash script.sh
|
|
|
Hello World!
|
|
|
Hello World!
|
|
|
Hello World!
|
|
|
```
|
|
|
|
|
|
Функции, так же, как и сами скрипты, могут принимать аргументы. Они имеют такие же названия, но аргументы функций видны только внутри функции, в которую они были переданы:
|
|
|
|
|
|
```sh
|
|
|
echo "$1" # аргумент переданный при запуске скрипта
|
|
|
|
|
|
calc () {
|
|
|
echo "$1 + $2 = $(($1 + $2))"
|
|
|
}
|
|
|
|
|
|
# передача двух аргументов в функцию calc
|
|
|
calc 42 17
|
|
|
```
|
|
|
```
|
|
|
$ bash script.sh hello
|
|
|
hello
|
|
|
42 + 17 = 59
|
|
|
```
|
|
|
|
|
|
## Локальные переменные
|
|
|
|
|
|
Если мы объявим какую-либо переменную, а затем объявим ещё одну с таким же именем, но уже внутри функции, то у нас произойдет перезапись:
|
|
|
|
|
|
```sh
|
|
|
VALUE="hello"
|
|
|
|
|
|
test() {
|
|
|
VALUE="linux"
|
|
|
}
|
|
|
|
|
|
test
|
|
|
echo $VALUE
|
|
|
```
|
|
|
```
|
|
|
$ bash script.sh
|
|
|
linux
|
|
|
```
|
|
|
|
|
|
Чтобы предотвратить такое поведение используются ключевое слово `local` перед именем переменной, которая объявляется внутри функции:
|
|
|
|
|
|
```sh
|
|
|
VALUE="hello"
|
|
|
|
|
|
test() {
|
|
|
local VALUE="linux"
|
|
|
echo "Переменная внутри функции: $VALUE"
|
|
|
}
|
|
|
|
|
|
test
|
|
|
echo "Глобальная переменная: $VALUE"
|
|
|
```
|
|
|
```
|
|
|
$ bash script.sh
|
|
|
Переменная внутри функции: linux
|
|
|
Глобальная переменная: hello
|
|
|
```
|
|
|
|
|
|
## Ключевое слово readonly
|
|
|
|
|
|
По умолчанию, каждая созданная переменная в Bash в последующем может перезаписываться. Чтобы защитить переменную от изменений можно использовать ключевое слово `readonly`:
|
|
|
|
|
|
```sh
|
|
|
readonly PI=3.14
|
|
|
PI=100
|
|
|
|
|
|
echo "PI = $PI"
|
|
|
```
|
|
|
```
|
|
|
$ bash script.sh
|
|
|
script.sh: строка 2: PI: переменная только для чтения
|
|
|
PI = 3.14
|
|
|
```
|
|
|
|
|
|
`readonly` можно использовать не только в момент объявления переменной, но и после:
|
|
|
|
|
|
```sh
|
|
|
VALUE=123
|
|
|
VALUE=$(($VALUE * 1000))
|
|
|
readonly VALUE
|
|
|
VALUE=555
|
|
|
|
|
|
echo $VALUE
|
|
|
```
|
|
|
```
|
|
|
$ bash script.sh
|
|
|
script.sh: строка 4: VALUE: переменная только для чтения
|
|
|
123000
|
|
|
```
|
|
|
|
|
|
Тоже самое касается функций. Они так же могут быть переопределены, поэтому их можно защитить с помощью `readonly` указав при этом флаг `-f`:
|
|
|
|
|
|
```sh
|
|
|
test() {
|
|
|
echo "This is test function"
|
|
|
}
|
|
|
|
|
|
readonly -f test
|
|
|
|
|
|
test() {
|
|
|
echo "Hello World!"
|
|
|
}
|
|
|
|
|
|
test
|
|
|
```
|
|
|
```
|
|
|
$ bash script.sh
|
|
|
script.sh: строка 9: test: значение функции можно только считать
|
|
|
This is test function
|
|
|
```
|
|
|
|
|
|
## Обработка сигналов
|
|
|
|
|
|
Во время выполнения скриптов, могут происходить неожиданные действия. Например, пользователь может прервать выполнения скрипта с помощь комбинации `Ctrl + C`, либо может случайно закрыть терминал или в самом скрипте может случится какая-либо ошибка и так далее...
|
|
|
|
|
|
В POSIX-системах существуют специальные сигналы - уведомления процесса о каком-либо событии. Их список определен в таблице ниже:
|
|
|
|
|
|
| Сигнал | Код | Действие | Описание |
|
|
|
| ------- | -------- | -------------------------- | -------------------------------------------------------- |
|
|
|
| SIGHUP | 1 | Завершение | Закрытие терминала |
|
|
|
| SIGINT | 2 | Завершение | Сигнал прерывания (Ctrl-C) с терминала |
|
|
|
| SIGQUIT | 3 | Завершение с дампом памяти | Сигнал «Quit» с терминала (Ctrl-) |
|
|
|
| SIGILL | 4 | Завершение с дампом памяти | Недопустимая инструкция процессора |
|
|
|
| SIGABRT | 6 | Завершение с дампом памяти | Сигнал, посылаемый функцией abort() |
|
|
|
| SIGFPE | 8 | Завершение с дампом памяти | Ошибочная арифметическая операция |
|
|
|
| SIGKILL | 9 | Завершение | Процесс уничтожен (kill signal) |
|
|
|
| SIGSEGV | 11 | Завершение с дампом памяти | Нарушение при обращении в память |
|
|
|
| SIGPIPE | 13 | Завершение | Запись в разорванное соединение (пайп, сокет) |
|
|
|
| SIGALRM | 14 | Завершение | Сигнал истечения времени, заданного alarm() |
|
|
|
| SIGTERM | 15 | Завершение | Сигнал завершения (сигнал по умолчанию для утилиты kill) |
|
|
|
| SIGUSR1 | 30/10/16 | Завершение | Пользовательский сигнал № 1 |
|
|
|
| SIGUSR2 | 31/12/17 | Завершение | Пользовательский сигнал № 2 |
|
|
|
| SIGCHLD | 20/17/18 | Игнорируется | Дочерний процесс завершен или остановлен |
|
|
|
| SIGCONT | 19/18/25 | Продолжить выполнение | Продолжить выполнение ранее остановленного процесса |
|
|
|
| SIGSTOP | 17/19/23 | Остановка процесса | Остановка выполнения процесса |
|
|
|
| SIGTSTP | 18/20/24 | Остановка процесса | Сигнал остановки с терминала (Ctrl-Z) |
|
|
|
| SIGTTIN | 21/21/26 | Остановка процесса | Попытка чтения с терминала фоновым процессом |
|
|
|
| SIGTTOU | 22/22/27 | Остановка процесса | Попытка записи на терминал фоновым процессом |
|
|
|
|
|
|
В Bash есть ключевое слово `trap` с помощью которого можно отлавливать различные сигналы и предусматривать выполнение определенных команд:
|
|
|
|
|
|
```
|
|
|
trap <КОМАНДА> <СИГНАЛ>
|
|
|
```
|
|
|
> Под сигналом можно использовать его название (колонка _Сигнал_ в таблице), либо его код (колонка _Код_ в таблице). Можно указывать несколько сигналов разделяя их названия или коды пробелом. <br>
|
|
|
> **Исключения:** сигналы SIGKILL (9) и SIGSTOP (17/19/23) отловить невозможно, поэтому нет смысла их указывать.
|
|
|
|
|
|
```sh
|
|
|
trap "echo Выполнение программы прервано...; exit" SIGINT
|
|
|
|
|
|
for i in {1..10}
|
|
|
do
|
|
|
sleep 1
|
|
|
echo $i
|
|
|
done
|
|
|
```
|
|
|
```
|
|
|
$ bash script.sh
|
|
|
1
|
|
|
2
|
|
|
3
|
|
|
4
|
|
|
^CВыполнение программы прервано...
|
|
|
```
|
|
|
|
|
|
## Отладка скриптов
|
|
|
|
|
|
Запуск скрипта с параметром `-x` покажет его поэтапное выполнение, что будет полезно при отладке и поиске ошибок:
|
|
|
|
|
|
```
|
|
|
$ bash -x script.sh
|
|
|
```
|
|
|
|
|
|
<div align="right"><a href="https://github.com/cheatsnake/backend-cheats/blob/master/files/linux/bash-scripts-cheatsheet.md#%D1%88%D0%BF%D0%B0%D1%80%D0%B3%D0%B0%D0%BB%D0%BA%D0%B0-%D0%BF%D0%BE-bash-%D1%81%D0%BA%D1%80%D0%B8%D0%BF%D1%82%D0%B0%D0%BC">Вернуться в начало ⬆️</a></div> |