Основы Bash-скриптинга для непрограммистов. Часть 3

от автора

Во второй части статьи мы обсудили файлы скриптов, их параметры и права доступа. Также поговорили про операторы условного выполнения, выбора и циклы. В этой, заключительной части мы рассмотрим функции и планировщик заданий cron. Также приведу различные полезные команды и ссылки.

Функции

Часто используемые, повторяющиеся блоки имеет смысл выделять в отдельные функции, чтобы при необходимости их запускать, передавая параметры.

Определение функции выглядит следующим образом:

<имя_функции>() {   <команды>   return <число> }  function <имя_функции>() {   <команды>   return <число> }

Первый вариант ближе к синтаксису языка С и считается более переносимым, во втором варианте круглые скобки можно не указывать. Также может отсутствовать оператор return, если функция не возвращает значения.

Самый простой пример скрипта, содержащего объявление и вызов функции будет выглядеть так:

#!/bin/bash  f() {   echo Test }  f

Мы объявили функцию f, которая выводит слово Test, и затем вызвали её:

test@osboxes:~$ ./script6.sh Test

Так же, как и скрипт, функция может принимать параметры и использовать их, ссылаясь по номеру ($1, $2, …, $N). Вызов функции с параметрами в скрипте осуществляется так:

<имя функции> <параметр1> <параметр2>… <параметрN>

Функция может возвращать результат своего выполнения (код завершения) в виде числового значения в диапазоне от 0 до 255. Принято считать, что если функция возвращает 0, то она выполнилась успешно, во всех остальных случаях значение содержит код ошибки. Чтобы получить код завершения функции в скрипте, необходимо обратиться к переменной $?. Добавив параметры и возвращаемое значение, получим следующий скрипт:

#!/bin/bash  summ() {   re='^[0-9]+$'   if ! [[ $1 =~ $re ]] ; then     return 1   elif ! [[ $2 =~ $re ]] ; then     return 2   else     s=$(($1 + $2))     return 0   fi }  summ $1 $2  case $? in  0) echo "The sum is: $s" ;;  1) echo "var1 is not a nubmer" ;;  2) echo "var2 is not a nubmer" ;;  *) echo "Unknown error" ;; esac

Здесь мы создали функцию summ, которая принимает 2 параметра и с помощью регулярного выражения ^[0-9]+$ проверяет, является ли каждый из переданных параметров числом. В случае, если первый параметр не число, то код завершения функции будет 1, если второй параметр не число, то код завершения функции будет 2. Во всех остальных случаях функция вычисляет сумму переданных параметров, сохраняя результат в глобальной переменной s.

Скрипт вызывает функцию, передавая ей на вход параметры, которые были переданы ему самому при вызове. Далее проверяется код завершения функции и выдается соответствующая ошибка, если код не равен 0, иначе выдается сумма, сохраненная в переменной s. Протестируем скрипт:

test@osboxes.org:~$ ./script7.sh abc 123 var1 is not a nubmer test@osboxes.org:~$ ./script7.sh 234 def var2 is not a nubmer test@osboxes.org:~$ ./script7.sh 10 15 The sum is: 25

По умолчанию переменные объявляются глобальными, т.е. видны в любом блоке скрипта. Переменные, объявленные как локальные, имеют ограниченную область видимости, и видны только в пределах блока, в котором они были объявлены. В случае с функцией это означает, что локальная переменная «видна» только в теле функции, в которой она была объявлена.

Для того, чтобы объявить переменную локальной, используется слово local, например local loc_var=123. Важно отметить, все что переменные, объявляемые в теле функции, считаются необъявленными до тех пор, пока эта функция не будет вызвана.

Объединим все воедино, создав на основе рассмотренных ранее структур следующий скрипт:

#!/bin/bash  clearFiles() {   rm *.dat   if [ $? -eq 0 ]   then     echo Files deleted   fi }  genFiles() {   for (( i=1; i<=$1; i++ ))   do     head -c ${i}M </dev/urandom >myfile${i}mb.dat   done   ls -l *.dat }  delFiles() { for f in *.dat   do     size=$(( $(stat -c %s $f) /1024/1024 ))     if [ $size -gt $1 ]     then       rm $f       echo Deleted file $f     fi   done   ls -l *.dat }  showWeather() {   curl -s "https://weather-broker-cdn.api.bbci.co.uk/en/observation/rss/$1" | grep "<desc" | sed -r 's/<description>//g; s/<\/description>//g' }  menu() {   clear   echo 1 - Delete all .dat files   echo 2 - Generate .dat files   echo 3 - Delete big .dat files   echo 4 - List all files   echo 5 - Planet info   echo 6 - Show weather   echo "x/q - Exit"   echo -n "Choose action: "   read -n 1 key   echo }  while true do   case "$key" in     "x" | "q" | "X" | "Q") break ;;     "1")       clearFiles       read -n 1     ;;     "2")       echo -n "File count: "       read count       genFiles $count       read -n 1     ;;     "3")       echo -n "Delete file greater than (mb): "       read maxsize       delFiles $maxsize       read -n 1     ;;     "4")       ls -la       read -n 1     ;;     "5")       ./script4.sh       read -n 1     ;;     "6")       echo -n "Enter city code: " # 524901 498817 5391959       read citycode       showWeather $citycode       read -n 1     ;;   esac   menu done

В данном скрипте мы объявили 5 функций:

  • clearFiles

  • genFiles

  • delFiles

  • showWeather

  • menu

Далее реализован бесконечный цикл с помощью оператора while с условием true, в который вложен оператор выбора в зависимости от нажатой клавиши, а также вызов функции menu для отображения списка доступных действий. Данный скрипт в интерактивном режиме позволяет выполнить следующие действия:

  • Удалить все файлы .dat в текущей директории

  • Создать указанное количество файлов

  • Удалить файлы больше определенного размера

  • Вывести список всех файлов текущей директории

  • Запустить скрипт, выдающий информацию о планетах

  • Отобразить погоду по коду указанного города

Не будем подробно разбирать все строки кода, скажем только, что в скрипте демонстрируется вызов другого скрипта, получение информации из интернет, и её парсинг (выделение нужной информации), команда break для выхода из цикла и ряд других возможностей. Предлагаю желающим самостоятельно протестировать скрипт, посмотреть, какие могут быть ошибки при его работе и при вводе различных значений, какие проверки можно добавить, и что можно улучшить. Приведем результаты тестирования:

test@osboxes.org:~$ ./script8.sh 1 - Delete all .dat files 2 - Generate .dat files 3 - Delete big .dat files 4 - List all files 5 - Planet info 6 - Show weather x/q - Exit Choose action: 4 total 40 drwxr-xr-x 2 test test 4096 Feb 16 15:56 . drwxr-xr-x 6 root root 4096 Feb 16 15:54 .. -rw------- 1 test test   42 Feb 16 15:55 .bash_history -rw-r--r-- 1 test test  220 Feb 16 15:54 .bash_logout -rw-r--r-- 1 test test 3771 Feb 16 15:54 .bashrc -rw-r--r-- 1 test test  807 Feb 16 15:54 .profile -rw-r--r-- 1 test test 1654 Feb 16 12:40 input.xml -rwxr-xr-x 1 test test  281 Feb 16 14:02 script4.sh -rwxr-xr-x 1 test test  328 Feb 16 13:40 script7.sh -rwxr-xr-x 1 test test 1410 Feb 16 15:24 script8.sh
1 - Delete all .dat files 2 - Generate .dat files 3 - Delete big .dat files 4 - List all files 5 - Planet info 6 - Show weather x/q - Exit Choose action: 2 File count: 8 -rw-rw-r-- 1 test test 1048576 Feb 16 16:00 myfile1mb.dat -rw-rw-r-- 1 test test 2097152 Feb 16 16:00 myfile2mb.dat -rw-rw-r-- 1 test test 3145728 Feb 16 16:00 myfile3mb.dat -rw-rw-r-- 1 test test 4194304 Feb 16 16:00 myfile4mb.dat -rw-rw-r-- 1 test test 5242880 Feb 16 16:00 myfile5mb.dat -rw-rw-r-- 1 test test 6291456 Feb 16 16:00 myfile6mb.dat -rw-rw-r-- 1 test test 7340032 Feb 16 16:00 myfile7mb.dat -rw-rw-r-- 1 test test 8388608 Feb 16 16:00 myfile8mb.dat
1 - Delete all .dat files 2 - Generate .dat files 3 - Delete big .dat files 4 - List all files 5 - Planet info 6 - Show weather x/q - Exit Choose action: 3 Delete file greater than (mb): 5 Deleted file myfile6mb.dat Deleted file myfile7mb.dat Deleted file myfile8mb.dat -rw-rw-r-- 1 test test 1048576 Feb 16 16:00 myfile1mb.dat -rw-rw-r-- 1 test test 2097152 Feb 16 16:00 myfile2mb.dat -rw-rw-r-- 1 test test 3145728 Feb 16 16:00 myfile3mb.dat -rw-rw-r-- 1 test test 4194304 Feb 16 16:00 myfile4mb.dat -rw-rw-r-- 1 test test 5242880 Feb 16 16:00 myfile5mb.dat
1 - Delete all .dat files 2 - Generate .dat files 3 - Delete big .dat files 4 - List all files 5 - Planet info 6 - Show weather x/q - Exit Choose action: 1 Files deleted
1 - Delete all .dat files 2 - Generate .dat files 3 - Delete big .dat files 4 - List all files 5 - Planet info 6 - Show weather x/q - Exit Choose action: 5 Enter the name of planet: Mars The Mars has two satellite(s).
1 - Delete all .dat files 2 - Generate .dat files 3 - Delete big .dat files 4 - List all files 5 - Planet info 6 - Show weather x/q - Exit Choose action: 6 Enter city code: 524901     Latest observations for Moscow from BBC Weather, including weather, temperature and wind information       Temperature: -11°C (11°F), Wind Direction: Northerly, Wind Speed: 0mph, Humidity: 84%, Pressure: 1018mb, , Visibility: Moderate

Примечание: для тестирования работы с данными из Интернет (пункт 6 в меню выбора скрипта) может потребоваться установка curl, это можно сделать командой sudo apt install curl.

Планировщик заданий cron

В случае, когда есть необходимость периодического запуска скриптов, полезно использовать планировщик cron, он позволяет задать расписание запуска скрипта и не требует присутствия администратора.

Просмотр заданий пользователя выполняется командой crontab –l. Для редактирования и создания новых задания используется команда crontab –e. Строки для запуска команд планировщика в файле конфигурации cron имеют следующий формат:

m h dom mon dow command parameters

Где m – минута, h – час, dom – день месяца, mon – месяц, dow – день недели, command – команда, parameters – список параметров. Наглядно этот формат можно представить так:

Например, для того, чтобы в 10 и 30 минут каждого часа каждый день месяца весь год по будням запускать команду, нужно указать следующее:

10,30 * * * 1-5 command parameter1 parameter2

Более простой пример, каждые 15 минут выполнять команду:

*/15 * * * * command

Создадим скрипт для резервного копирования домашней директории пользователя, который будет создавать новый файл бэкапа при каждом запуске:

#!/bin/bash USER=`whoami` BACKUP_DIR=/tmp/backup_${USER} BACKUP_FILE=${USER}_$(date +%Y%m%d%M%H%S).tgz  mkdir -p $BACKUP_DIR cd / tar -zcf $BACKUP_DIR/$BACKUP_FILE home/$USER

Поставим скрипт на выполнение каждый день в 22:00, выполнив команду crontab -eи добавив с помощью открывшегося редактора строку:

00 22 * * * ./backup_home.sh

Проверить, что задача добавлена в планировщик, можно командой crontab -l:

test@osboxes.org:~$ crontab -l 00 22 * * * ./backup_home.sh

В результате каждый день в 22:00 будет создаваться резервная копия домашней директории пользователя (в приведенном примере для демонстрации запуск скрипта выполняется каждую минуту):

test@osboxes.org:~$ cd /tmp/backup_test/ test@osboxes:/tmp/backup_test$ ll total 80 drwxrwxr-x  2 test test 4096 Feb 16 16:38 ./ drwxrwxrwt 17 root root 4096 Feb 16 16:30 ../ -rw-rw-r--  1 test test 4431 Feb 16 16:30 test_20210216301601.tgz -rw-rw-r--  1 test test 4431 Feb 16 16:31 test_20210216311601.tgz -rw-rw-r--  1 test test 4431 Feb 16 16:32 test_20210216321601.tgz -rw-rw-r--  1 test test 4431 Feb 16 16:33 test_20210216331601.tgz -rw-rw-r--  1 test test 4431 Feb 16 16:34 test_20210216341601.tgz test@osboxes:/tmp/backup_test$

Нужно отметить, что директория /tmp в примере использована исключительно для тестов, т.к. она предназначена для хранения временных файлов, и использовать её для хранения резервных копий нельзя. Правильное место размещения бэкапов может подсказать системный администратор.

Список полезных команд

Список встроенных команд интерпретатора: help
Помощь по команде: <команда> —help
Мануал по команде: man <команда>
Версия команды: <команда> —version
Список доступных оболочек: cat /etc/shells
Список пользователей и их оболочек: cat /etc/passwd
Текущая директория: pwd
Список файлов текущей директории: ls -la
Текущий пользователь: id
Переменные среды: set
Версия ОС: cat /etc/os-release
Версия ядра: uname -a
Получить привилегии суперпользователя: sudo su —
Установка программы в Debian: apt install mc
Посмотреть утилизацию(загрузку): top
Свободное место: df -h
Сколько занимает директория: du -ks /var/log
Конфигурация сетевых интерфейсов: ifconfig -a
Объем оперативной памяти: free -m
Информация о блочных устройствах(дисках): lsblk
Информация о процессорах: cat /proc/cpuinfo
Список установленных пакетов: apt list —installed
Список и статус сервисов: service —status-all
Перезапуск сервиса: service apache2 restart
Скачать файл: wget https://www.gnu.org/graphics/gplv3-with-text-136×68.png
Получить веб-страницу по URL: curl https://www.google.com
Показать задания планировщика: crontab -l
Редактировать задания планировщика: crontab -e
Вывести новые сообщения в системном логе: tail -f /var/log/syslog
Подсчитать количество строк в выводе команды: <команда> | wc -l
Изменить права доступа к файлу (разрешить выполнение всем): chmod a+x <файл>
Список процессов: ps -ef
Проверить, запущен ли процесс: ps -ef | grep <процесс>
Перейти в предыдущий каталог: cd —
Завершить процесс (сигнал kill): kill -9
Удаление файла: rm <имя файла>
Удаление директории: rm -rf <имя директории>
Редактировать файл: nano <имя_файла>
Топ 10 процессов по использованию памяти: ps aux | awk ‘{print $6/1024 » MB\t\t» $11}’ | sort -nr | head

Полезные ссылки

Руководство по bash: GNU Bash manual
Расширенное руководство по Bash: Advanced Bash-Scripting Guide
Статья на Википедии: Bash
Описание команд и утилит оболочки bash: SS64
Часто задаваемые вопросы о Debian GNU/Linux: Debian FAQ

Заключение

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

На этом пока все, надеюсь, было интересно!
Какие другие полезные команды вы знаете и используете в работе?
Какие интересные конструкции приходилось использовать?
Какие задачи решали?
Всем удачного скриптинга, делитесь мнениями в комментариях!

ссылка на оригинал статьи https://habr.com/ru/post/542744/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *