fbpx
linux
29

Программирование Bash. Часть третья

0
(0)

Продолжаем осваивать базовые приёмы программирования Bash и изучаем циклы в Bash, а также арифметические вычисления!

bash циклы

Программирование Bash. Предыдущая часть статьи

Оглавление:

  1. Циклы Bash
  2. Цикл For
  3. Цикл While
  4. Цикл Until
  5. Вычисления в Bash: expr, let, bc
  6. Заключение

Циклы в Bash

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

Проблема возникает только тогда, когда нам необходимо ежедневно бекапить множество пользовательских каталогов. Таким образом, задача очень быстро станет утомительной и отнимающей много времени. На данном этапе было бы неплохо иметь средства для резервного копирования любого количества выбранных домашних каталогов пользователей с помощью единственного выполнения скрипта backup.sh.

К счастью, наша любимая оболочка позаботилась об этом, поскольку эта задача может быть решена с помощью циклов Bash.
Циклы Bash - это циклические конструкции, используемые для итерационного выполнения (перебора) любого заданного количества задач до тех пор, пока не будут выполнены все пункты в указанном списке или же предопределенные условия. Циклы в Bash имеют три основных типа.

Цикл For

Цикл for используется для повторения любого заданного кода для любого количества элементов в заданном списке. Давайте начнём с простого примера цикла For:

Приведенный выше цикл for использовал команду echo для печати всех элементов 1, 2 и 3 в списке. Использование точки с запятой позволяет нам выполнить цикл for в одной строке. Если бы мы перенесли приведенный выше цикл for в скрипт bash, код выглядел бы следующим образом:

#!/bin/bash

for i in 1 2 3; do
    echo $i
done

Цикл for состоит из четырех Shell Reserved Words (слова со специальным значением в Shell): for, in, do, done.. Так что можно прочитать код выше следующим образом: для каждого элемента в списке 1, 2 и 3 присвоить значение элемента временно переменной i после чего выполнить echo $i чтобы вывести элемент в поток STDOUT (по умолчанию - экран) и продолжить выводить пока не выполнится для каждого элемента.

Выводить числа, конечно, весело, но давайте попробуем сделать что-то более полезное. Используя подстановку команд, как объяснялось ранее в этой статье, мы можем создать любой список, который будет частью конструкции цикла for. Следующий немного более сложный пример цикла for будет подсчитывать символы в каждой строке для любого заданного файла:

Да, мощь GNU Bash не знает границ, как только вы освоите её! Не спешите экспериментировать, прежде чем двигаться дальше.

Упражнение:

Перепишите приведенный выше цикл for для подсчёта символов так, чтобы он выводил имена всех файлов и каталогов в текущем рабочем каталоге вместе с количеством символов, из которых состоит имя каждого файла и каталога. Вывод цикла for должен выглядеть таким образом:
0_xvz = 5
backup.sh = 9
compare.sh = 10
date.sh = 7
file1.txt = 9
foobar = 6
function.sh = 11
hello-world.sh = 14
if_else.sh = 10
items.txt = 9

Цикл While

Следующий вид циклов в нашем списке - цикл while. Конкретно этот цикл действует по заданному условию. То есть он будет выполнять код, заключенный в рамки DO и DONE пока заданное условие истинно. Как только заданное условие станет ложным, выполнение цикла прекратится. Рассмотрим следующий пример:

#!/bin/bash

counter=0
while [ $counter -lt 3 ]; do
    let counter+=1
    echo $counter
done

Этот цикл while будет продолжать выполнять указанный код только до тех пор, ПОКА переменная счетчика будет меньше 3. Это условие задается в 4-ой строке. Во время каждой итерации цикла (строка 5), переменная счётчик увеличивается на единицу. Как только счётчик станет равным 3, условие, заданное в строке 4, станет ложным и выполнение цикла while завершится.

Цикл Until

Последний цикл, который мы рассмотрим в этой статье по написанию скриптов - это цикл until. Цикл until действует прямо противоположно циклу while. Цикл until также действует по заданному условию. Однако код, заключенный между DO и DONE, будет выполняться только до тех пор, пока это условие не изменится с ложного на истинное. Выполнение цикла until проиллюстрировано на следующем примере:

#!/bin/bash

counter=6
until [ $counter -lt 3 ]; do
    let counter-=1
    echo $counter
done

Если вы поняли приведенный выше скрипт цикла while, то цикл until станет в некоторой степени понятным. Скрипт начинается с переменной counter, со значением 6. Условие, определенное в строке 4 этого конкретного цикла until, состоит в том, чтобы продолжать выполнять код до тех пор, пока условие не станет истинным.

На этом шаге можно применить наше понимание циклов. Наш текущий скрипт бекапов в настоящее время способен создавать резервные копии одного каталога за одно выполнение. Но было бы неплохо иметь возможность создавать резервные копии всех каталогов, передаваемых скрипту в командной строке при его выполнении. Ниже приведен обновленный скрипт, где такая возможность уже реализована:

#!/bin/bash

# This bash script is used to backup a user's home directory to /tmp/.

function backup {

    if [ -z $1 ]; then
        user=$(whoami)
    else 
        if [ ! -d "/home/$1" ]; then
            echo "Requested $1 user home directory doesn't exist."
            exit 1
        fi
        user=$1
    fi 

    input=/home/$user
    output=/tmp/${user}_home_$(date +%Y-%m-%d_%H%M%S).tar.gz

    function total_files {
        find $1 -type f | wc -l
    }

    function total_directories {
        find $1 -type d | wc -l
    }

    function total_archived_directories {
        tar -tzf $1 | grep  /$ | wc -l
    }

    function total_archived_files {
        tar -tzf $1 | grep -v /$ | wc -l
    }

    tar -czf $output $input 2> /dev/null

    src_files=$( total_files $input )
    src_directories=$( total_directories $input )

    arch_files=$( total_archived_files $output )
    arch_directories=$( total_archived_directories $output )

    echo "########## $user ##########"
    echo "Files to be included: $src_files"
    echo "Directories to be included: $src_directories"
    echo "Files archived: $arch_files"
    echo "Directories archived: $arch_directories"

    if [ $src_files -eq $arch_files ]; then
        echo "Backup of $input completed!"
        echo "Details about the output backup file:"
        ls -l $output
    else
        echo "Backup of $input failed!"
    fi
}

for directory in $*; do
    backup $directory 
done;

Просмотрев скрипт выше, вы, возможно, заметили, что в строках 5-57 была создана новая функция под названием backup. Эта функция включает в себя весь наш ранее написанный код. Определение функции заканчивается на строке 57, после чего мы реализовали новый цикл for на строках 59 - 51 для выполнения новой функции backup для каждого пользовательского каталога, предоставленного в качестве аргумента. Если вы помните, переменная **$** содержит все аргументы, передаваемые в командной строке при выполнении сценария. Кроме того, косметическое изменение кода в строке 44* обеспечивает лучшую читаемость вывода скрипта, разделяя каждый блок вывода информации о резервном копировании каталога хэш-строкой. Давайте посмотрим, как это работает:

$ ./backup.sh linuxconfig damian
########## linuxconfig ##########
Files to be included: 27
Directories to be included: 4
Files archived: 27
Directories archived: 4
Backup of /home/linuxconfig completed!
Details about the output backup file:
-rw-r--r-- 1 linuxconfig linuxconfig 236173 Oct 23 10:22 /tmp/linuxconfig_home_2017-10-23_102229.tar.gz
########## damian ##########
Files to be included: 3
Directories to be included: 1
Files archived: 3
Directories archived: 1
Backup of /home/damian completed!
Details about the output backup file:
-rw-r--r-- 1 linuxconfig linuxconfig 2140 Oct 23 10:22 /tmp/damian_home_2017-10-23_102230.tar.gz

Упражнение:

Текущий скрипт не проверяет существование пользовательских каталогов перед выполнением функции резервного копирования. Это может привести к непредвиденным последствиям. Как вы думаете, сможете ли вы создать свою собственную улучшенную копию скрипта резервного копирования, определив отдельный цикл для проверки существования всех пользовательских каталогов до того, как будет достигнут цикл for с  резервным копированием? Пусть цикл for завершает выполнение сценария, если какой-либо из пользовательских каталогов из предоставленного списка не существует.

Вычисления в Bash

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

Арифметическое расширение

Арифметическое расширение - это, пожалуй, самый простой метод выполнения основных вычислений. Мы просто заключаем любое математическое выражение в двойные круглые скобки. Давайте выполним несколько простых вычислений сложения, вычитания, умножения и деления с целыми числами:

Упражнение:

Cможете  использовать метод выше для выполнения операций по модулю? Например, каков результат операции 99 % 10 с абсолютным значением?

Команда expr

Другой альтернативой двойным скобкам является команда expr. Использование команды expr позволяет выполнить арифметическую операцию, даже не заключая математическое выражение в скобки или кавычки. Однако не забудьте убрать знак звездочки, чтобы избежать expr: syntax error.
:

Команда let

Аналогично команде expr, мы можем выполнять вычисления в bash с помощью команды let. Команда let считает математическое выражение и сохраняет его результат в переменную. Мы уже сталкивались с командой let в одном из наших предыдущих примеров, где она использовалась для выполнения целочисленного инкремента. В следующем примере показаны некоторые основные операции с использованием команды let, а также увеличение целого числа и операции со степенью, например x³:

Команда bc

Спустя несколько минут экспериментирования с приведенными выше методами вычислений bash вы, возможно, заметили, что они прекрасно работают с целыми числами, однако когда дело доходит до десятичных дробей, происходит что-то неладное. Чтобы поднять вычисления в bash на совершенно другой уровень, нам понадобится команда bc. Команда bc с правильным синтаксисом позволяет выполнять не только простые вычисления целых чисел.

Руководство по использованию команды bc довольно обширно, оно занимает более 500 строк. Однако не помешает показать некоторые базовые операции. Следующий пример выполнит операцию деления с результатом с двумя и тридцатью знаками после запятой и вычислит квадратный корень из 50 с пятьюдесятью знаками после запятой. По умолчанию команда bc выдает все результаты в виде целого числа. Используйте scale=x, чтобы указать команде bc показывать вещественные числа:

Давайте применим наши новые знания вычислений в bash и еще раз изменим наш сценарий backup.sh, чтобы реализовать счетчик всех заархивированных файлов и каталогов для всех пользователей:

#!/bin/bash

# This bash script is used to backup a user's home directory to /tmp/.
function backup {

    if [ -z $1 ]; then
        user=$(whoami)
    else 
        if [ ! -d "/home/$1" ]; then
                echo "Requested $1 user home directory doesn't exist."
                exit 1
        fi
        user=$1
    fi 

    input=/home/$user
    output=/tmp/${user}_home_$(date +%Y-%m-%d_%H%M%S).tar.gz

    function total_files {
        find $1 -type f | wc -l
    }

    function total_directories {
        find $1 -type d | wc -l
    }

    function total_archived_directories {
        tar -tzf $1 | grep  /$ | wc -l
    }

    function total_archived_files {
        tar -tzf $1 | grep -v /$ | wc -l
    }

    tar -czf $output $input 2> /dev/null

    src_files=$( total_files $input )
    src_directories=$( total_directories $input )

    arch_files=$( total_archived_files $output )
    arch_directories=$( total_archived_directories $output )

    echo "########## $user ##########"
    echo "Files to be included: $src_files"
    echo "Directories to be included: $src_directories"
    echo "Files archived: $arch_files"
    echo "Directories archived: $arch_directories"

    if [ $src_files -eq $arch_files ]; then
        echo "Backup of $input completed!"
        echo "Details about the output backup file:"
        ls -l $output
    else
        echo "Backup of $input failed!"
    fi
}

for directory in $*; do
    backup $directory 
    let all=$all+$arch_files+$arch_directories
done;
    echo "TOTAL FILES AND DIRECTORIES: $all"

В строке 60 мы использовали дополнение для добавления всех архивированных файлов с помощью команды let в результирующую переменную all. Каждая итерация цикла for добавляет новый счетчик для каждого дополнительного пользователя. Результат выводится с помощью команды echo в строке 62.

Пример выполнения кода:

$ ./backup.sh linuxconfig damian
########## linuxconfig ##########
Files to be included: 27
Directories to be included: 6
Files archived: 27
Directories archived: 6
Backup of /home/linuxconfig completed!
Details about the output backup file:
-rw-r--r-- 1 linuxconfig linuxconfig 237004 Dec 27 11:23 /tmp/linuxconfig_home_2017-12-27_112359.tar.gz
########## damian ##########
Files to be included: 3
Directories to be included: 1
Files archived: 3
Directories archived: 1
Backup of /home/damian completed!
Details about the output backup file:
-rw-r--r-- 1 linuxconfig linuxconfig 2139 Dec 27 11:23 /tmp/damian_home_2017-12-27_112359.tar.gz
TOTAL FILES AND DIRECTORIES: 37

Упражнение:

Поэкспериментируйте с backup.sh. Скрипт далек от идеала, добавляйте новые возможности или исправляйте существующие. Не бойтесь что-то сломать, это совершенно нормально. Устранение неполадок и исправление кода - это, возможно, лучший стимул для того, чтобы углубить понимание скриптов в bash и улучшить способность писать скрипты сверх того, что было рассмотрено в этой статье.

Заключение

Сценарии оболочки bash - это нечто большее, чем рассмотрено в этой статье. Тем не менее, прежде чем двигаться дальше, убедитесь, что вам понятны рассмотренные здесь темы: циклы Bash и арифметика. Помимо "гугления", в Интернете есть огромное количество других ресурсов, которые помогут вам, если возникнут сложности. Наиболее известным и рекомендуемым из них является Справочное руководство по Bash от GNU.

Оригинал статьи тут

Насколько публикация полезна?

Нажмите на звезду, чтобы оценить!

Средняя оценка 0 / 5. Количество оценок: 0

Оценок пока нет. Поставьте оценку первым.

Сожалеем, что вы поставили низкую оценку!

Позвольте нам стать лучше!

Расскажите, как нам стать лучше?

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

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

Заполните поле
Заполните поле
Пожалуйста, введите корректный адрес email.
Вы должны согласиться с условиями для продолжения

Читают сейчас
Меню

Попробуй курс Linux и DevOPS