====== Скриптинг ======
Первой строкой указывается т.н. **shebang (#!)** - последовательность двух символов, далее указывается путь к исполняющему файлу под-оболочки, так же, в этой строке можно передавать аргументы.\\
Если оболочка не указана явно, используется так же, что и запускает данный скрипт.\\
Для исполнения скрипта, у файла должно быть разрешение выполнения (**+x**), но если передать скрипт аргументом команде **bash ./script**, тогда бит необязателен.\\
**exit** - сообщает результат **(0-успех)** работы родительскому процессу, при отсутствии используется результат последней команды.\\
===== Аргументы (доступ к ним) =====
* **$?** - код возврата последней выполнившейся команды
* **$$** - номер процесса последней команды ($! фоновой)
* **$№** - аргументы, по номеру. &0 - последний запущенный скрипт.
* **$#** - кол-во переданных аргументов.
* **$*** - все аргументы одной строкой.
* **$@** - все аргументы отдельной строкой.
* **$-** - дейтствующие пар-ры команды **set**
* **shift** - команда, после нее первый аргумент в $@ теряется, остальные сдвигаются влево.
===== Переменные =====
**Пробелы не используются при определении**\\
Знак доллара используется только **при чтении** значения, работа с переменной без этого знака.\\
В фигурных скобках более строгая форма ${variable}.\\
**Двойные кавычки** - "нестрогие", и не влияют на механизм подстановки, в отличии от **одинарных кавычек**.\\
Конкатенация строк происходит без дополнительных символов: **$Val1"_"$Val2**.
${переменная/шаблон/замена} - замена первого совпадения
${переменная/шаблон} - удалить первое совпадение
${переменная//шаблон/замена} - замена каждого совпадения (# в начале, % в конце переменной)
${string:position [:length] } - подстрока
${переменная:-значение} - значение по умолчанию для переменной
${переменная:=значение} - если переменной нет то присваиваем значение
${переменная:?значение} - использовать переменную если установлена, либо выйти с ошибкой (значение)
${переменная:+значение} - использовать значение если переменная установлена, иначе ничего
${переменная#шаблон} - значение переменной после удаления шаблона слева, самый короткий (## самый длинный) (% %% тоже самое только справа)
==== Массивы ====
**Индексные**\\
Инициализируется перечнем значений в круглых скобах, через пробел. Работает **отрицательный индекс**\\
a=(0 1 2 3 4 5)
# Первый / Четвертый / предпоследний элемент / второй
${a}; ${a[4]}; ${a[-2]}; ${a[3-1]}
# Все элементы массива
${arr[*]}; ${arr[@]};
# Ряд элементов массива
${#arr[]}; ${#arr[]};
# Длина массива
${#arr[@]}
**Ассоциативные**\\
# Объявить так
declare -A arr
arr[index_foo]=value_foo
arr[index_bar]=value_bar
arr[index_xyz]=value_xyz
# Либо так
array_name=(
[index_foo]=value_foo
[index_bar]=value_bar
[index_xyz]=value_xyz
)
# Перебор массива должен быть таким
# Обязательно с воскл знаком, иначе не те значения
for key in ${!from_49[@]}; do
==== Встроенные переменные ====
* **$BASH** - путь к исполняемому файлу bash.
* **$GROUPS** - группы пользователя.
* **$HOME ** - домашний каталог.
* **$HOSTNAME ** - сетевое имя хоста.
* **$SHLVL ** - уровень вложенности shell.
* **$SECONDS** - время работы скрипта.
* **$REPLY** - для ввода read, по умолчанию.
===== Условие =====
**Двойные квадратные скобки** работают в целом так же, как и [одинарные квадратные скобки], но имеют дополнительные возможности вроде лучшей поддержки регулярных выражений.\\
**Двойные круглые скобки** это конструкция, позволяющая осуществлять арифметические вычисления внутри Bash.\\
if [[ "$name" == "Ryan" ]] && ! [[ "$time" -lt 2000 ]]; then
elif [[ "$day" == "New Year's Eve" ]] || [[ "$coffee_intake" -gt 9000 ]]; then
else
fi
if [ "$age" -gt 30 ]; then
echo "What an oldy."
fi
(( count++ ))
echo "$count"
if (( -57 + 30 + 27 )); then
echo "First one"
echo $(( (5 > 3) + (0 == 0) ))
# В условии можно использовать **код завершения любой команды**\\
if grep -q coffee dialogue.txt; then .. fount/not found; fi
if cmp a b &> /dev/null; then ... fi
:!: Old
Проверяет, является ли результат **0 (истина)**.\\
Условие условие проверяется с помощью команды [[main:linux:bash:утилита_test|test]], на данный момент, команда **является встроенной** т.е. не вызывает аналогичную утилиту.\\
Условия можно указать следующими способами:
* **if test -z $1**
* **if /usr/bin/test -z $1**
* **if [ -z $1 ]**
* **if /usr/bin/[ -z $1**
* **if [[ -z $1 ]']** - расширенный вариант простых скобок. Внутри допустимы операторы && || < и >.
* **[ -z $1 ]** - условие может быть проверено из без оператора **if**
* **() или двойные** - выполняет арифметическое действие внутри. **Код возврата противоположен []**
Условный оператор проверяет код завершения **любой команды**, а не только результат выражения скобок.
if cmp a b
then echo "Файлы идентичны"
else echo "Файлы различаются"
fi
Оператор **if** можно и не использовать.
[ -z $1 ] && echo result false
или
ping -c 1 8.8.8.8 &>/dev/null || echo not available
==== Else if (elif) ====
**elif** - краткая форма записи конструкции **else if**.
if [ expr ]; then
action
elif [ expr ]; then
action
else
action
fi
===== Циклы =====
==== for ====
Обработка диапазонных значений.\\
**for** условие **do** действие **done**.
for (( i=100; i>1; 1-- )); do action; done
# Внутри **((..)')** вычисляется арифметическое выражение и возвращается результат и позволяет работать с переменными в стиле С. Так же, можно указать диапазон
for i in {100..104}; do action; done
for i in 100 101 102 103 104; do action; done
for i in $@; do action; done
numbers="1 2 3 4 5"
for i in `echo $numbers`; do action; done
# Перебор массива, выполнение команды с каждым элементом
list=(user1 user2 user3 user4 user5 user6)
for i in ${list[*]}; do
printf '\n=======%s\n', $i
influx -execute 'show grants for '$i';' -username '' -password ''
done
==== while ====
Выполняется пока условие истинно.\\
**while** условие **do** действие **done**.
В двойных скобках символ **$** перед переменной можно опустить, так же, двойные скобки позволяют наращивать значение переменной **( (v+=1) )**
while [ $v1 -le $v2 ] do done
while (( v1 <= v2 )') ; do action; done
:!: Примеры
Бесконечный цикл
while true
do
echo "--==" $(uname -a) "==--"
sleep 2
done
==== Until ====
Противоположно циклу **while**, выполняется пока условие ложно.
**until** условие **do** действие **done**.
Все остальное аналогично.
===== case =====
case $v1 in
val1)
action1;;
val2)
action2;;
*)
default action;;
esac
===== Работа со строками =====
# Длина строки
echo ${#string}
echo `expr length $string`
===== Отладка =====
Для отладки, можно использовать команду **bash -x файл_скрипта**.
===== Примеры =====
**"Около-многопоточность"**\\
Используется минимум два скрипта, в первом выполнение работы, второй в цикле запускает подоболочку в фоне, не дожидаясь окончания каждого. Ньюанс в том что в конце головной скрипт не закрывается сам, думаю можно исправить аргументами.
for CurrAddr in {1..25}; do
./oneping $CurrAddr & # Там происходит просто пинг переданного адреса
done
# В итоге, ждем тайм-аут один раз, для всех хостов
:!: Случайные значения
**Записать случайные байты в файл**\\
head -c 1024 /dev/urandom > file
dd if=/dev/urandom of=file bs=100M count=1 iflag=fullblock
# только печатные символы
tr -dc 'a-zA-Z0-9' < /dev/urandom | head -c 100M > file
# либо base64 вместо tr, только менее гибко по символам
base64 < /dev/urandom | head -c 1024 (1K/1M)
# ${RANDOM} - возвращает случайное число 0..32К
# случайное число от 1..10, (1+ чтобы небыло ноля)
$(1 + ${RANDOM} % 10)
# Еще так можно диапазон возвращать
# 1..5, вернуть одно число
$(shuf -i 1-5 -n 1)
===== Утилита test =====
Unix утилита для проверки типа файла и сравнения значений.\\
Возвращает 0 (истина) или 1 (ложь). Выражения могут быть как унарными так и бинарными.\\
# Использование
test [expr]
if test -f file.txt или [ -f file.txt ]; then
rm file.txt
else
echo 'no found'
fi
# Проверка передачи файла в первом аргументе
if (($# < 1)) || [ ! -f ${1} ]; then
echo "Config file must be set in the first agrument"
exit 1
fi
# Если первый аргмент пустой или отсутствует
if [ -z ${1} ]; then
# Если второй аргумент НЕ пустой и не отсутствует
if [ -n ${2} ]; then
# Если отсутствует или пустые первый или второй аргументы
if [ -z ${1} ] || [ -z ${2} ]; then
==== Параметры запуска ====
* **-d file** - если file существует и является директорией.
* **-e file** - если file существует.
* **-f file** - если file существует и является обычным файлом.
* **-k file** - если file существует и ему установлен "sticky" бит.
* **-L file** - если file существует и является символьной ссылкой.
* **-r (-w/-x) file** - если file существует и читаем (записываем/исполняем).
* **-z str** - если длина 0.
* **-n str** - если длина не 0.
* **-a (-o)** - аналог && (||) в одинарных скобках.
* **str1 = ('!=') str2** - если строки равны (не равны).
===== Функции. Возврат значения =====
**function_name() {command... }**\\
Объявляются раньше вызова, **нет возможности предобъявления**\\
**return** возвращает только интовое значение, до 255\\
===== Примеры =====
:!: Обширный пример
{{ :linux:overall:bash:makeimport_sh.doc |}}
:!: Проверка PID
# Такая конструкция сохранит ПИД последнего запущенного процесса в файл
& echo $! > file.pid
#!/bin/bash
status=2
for i in 1 2 3
do
# Для kill нужны права
#if kill -0 `cat /path/file.pid` &> /dev/null;
if ps -p `cat /path/file.pid` > /dev/null
then
status=0
break
fi
sleep 1
done
echo "Service status is $status"
exit $status
:!: Выполнение скрипта на удаленной машине через SSH
При условии что настроен беспарольный доступ\\
#!/bin/bash
ssh username@my-host -p 2444 'bash -s' <<-'ENDSSH'
if systemctl is-active --quiet "apache2"; then
sudo systemctl stop apache2
sudo systemctl start nginx
sudo systemctl status nginx
else
sudo systemctl stop nginx
sudo systemctl start apache2
sudo systemctl status apache2
fi
ENDSSH
Проверка процессов по списку PID
# Запуск фонового процесса с сохранением PID ($! для фонового процесса)
(/usr/bin/sleep infinity &) && (echo $! > pids)
for pid in `cat pids`; do
if ! ps -p $pid > /dev/null; then
echo "exit 1 for pid - $pid"
exit 1
fi
done
:!: