====== Скриптинг ====== Первой строкой указывается т.н. **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
:!: