fbpx
linux
50

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

0
(0)

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

Оглавление:

  1. Простой сценарий оболочки Bash для резервного копирования
  2. Переменные
  3. Перенаправления ввода, вывода и ошибок
  4. Функции
  5. Числовые и строковые сравнения
  6. Условные утверждения программирования Bash
  7. Позиционные параметры

Простой сценарий оболочки Bash для резервного копирования

Продолжаем программирование Bash. Давайте более подробно обсудим выполнение командной строки и то, как команды GNU/Linux вписываются в процесс создания сценария оболочки.

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

Быстрый совет:

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

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

Каждая команда имеет страницу руководства, из которой можно узнать о ее функциях, а также о том, какие опции и аргументы принимает каждая конкретная команда.

Используйте команду man, чтобы отобразить страницу руководства для любой нужной команды. Например, чтобы отобразить страницу руководства для команды ls, выполните man ls. Чтобы выйти из страницы руководства, нажмите клавишу q.

В приведенном ниже примере команды ls показано базовое использование опций и аргументов командной строки.

Хотя наш первый сценарий оболочки "Hello World" требует глубокого понимания создания, редактирования и выполнения файлов, его практичность можно поставить под сомнение.

Следующий пример предлагает более практическое применение, поскольку он может быть использован для резервного копирования домашнего каталога нашего пользователя. Для создания сценария резервного копирования, в строке 3 мы будем использовать команду tar с различными опциями -czf, чтобы создать сжатый tar всего домашнего каталога пользователя /home/linuxconfig/. Вставьте следующий код в новый файл backup.sh, сделайте скрипт исполняемым и запустите его:

#!/bin/bash

tar -czf /tmp/myhome_directory.tar.gz /home/linuxconfig

Быстрый совет:

Введите команду man tar, чтобы узнать больше обо всех опциях командной строки tar, использованных в предыдущем сценарии backup.sh. Попробуйте запустить команду tar без префикса опции -! Работает ли она?

Переменные в программировании Bash

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

#!/bin/bash

greeting="Welcome"
user=$(whoami)
day=$(date +%A)

echo "$greeting back $user! Today is $day, which is the best day of the entire week!"
echo "Your Bash shell version is: $BASH_VERSION. Enjoy!"

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

$ ./welcome.sh 
Welcome back linuxconfig! Today is Wednesday, which is the best day of the entire week!
Your Bash shell version is: 4.4.12(1)-release. Enjoy!

Давайте рассмотрим сценарий более подробно. Во-первых, мы объявили переменную greeting и присвоили ей строковое значение Welcome. Следующая переменная user содержит значение имени пользователя, запускающего сеанс оболочки. Это делается с помощью техники, называемой подстановкой команд. Это означает, что вывод команды whoami будет напрямую присвоен переменной user. То же самое относится и к нашей следующей переменной day, которая содержит имя сегодняшнего дня, полученное командой date +%A.

Вторая часть сценария использует команду echo для вывода сообщения, заменяя имена переменных, которые теперь имеют префикс $, соответствующими значениями. Если вы задаетесь вопросом о последней переменной $BASH_VERSION, знайте, что это так называемая внутренняя переменная, определенная как часть вашей оболочки.

Быстрый совет:

Никогда не называйте свои личные переменные, используя символы UPPERCASE. Это связано с тем, что имена переменных в верхнем регистре зарезервированы для внутренних переменных оболочки, и вы рискуете их перезаписать. Это может привести к дисфункции или неправильному выполнению сценария.

Переменные также можно использовать непосредственно в командной строке терминала. В следующем примере объявлены переменные a и b с целочисленными данными. Используя команду echo, мы можем вывести их значения или даже выполнить арифметическую операцию, как показано в следующем примере:

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

Кроме того, сценарий больше не будет привязан к определенному пользователю. Отныне наш сценарий backup.sh bash может быть запущен любым пользователем, при этом резервное копирование будет производиться в домашний каталог нужного пользователя:

#!/bin/bash

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

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

tar -czf $output $input
echo "Backup of $input completed! Details about the output backup file:"
ls -l $output

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

Во-вторых, в скрипте используется новый трюк сценариев оболочки ${параметр}, называемый расширением параметров. В нашем случае фигурные скобки {} необходимы, потому что за нашей переменной $user следуют символы, которые не являются частью имени переменной. Ниже приведен вывод нашего обновленного сценария резервного копирования:

$ ./backup.sh 
tar: Removing leading `/' from member names
Backup of /home/linuxconfig completed! Details about the output backup file:
-rw-r--r-- 1 linuxconfig linuxconfig 8778 Jul 27 12:30 /tmp/linuxconfig_home_2017-07-27_123043.tar.gz

Перенаправления ввода, вывода и ошибок в программировании Bash

Обычно команды, выполняемые в командной строке GNU/Linux, либо производят вывод, либо требуют ввода, либо выдают сообщение об ошибке. Это фундаментальная концепция для создания сценариев оболочки, а также для работы с командной строкой GNU/Linux в целом.

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

Больше всего нас интересует вывод обеих команд ls -l foobar. Обе команды выдали вывод, который по умолчанию отображается на вашем терминале. Однако оба вывода принципиально отличаются друг от друга.

Первая команда пытается вывести список несуществующего файла foobar, что, в свою очередь, приводит к выводу стандартной ошибки (stderr). Как только файл создан командой touch, второе выполнение команды ls выводит стандартный вывод (stdout).

Разница между выводами stdout и stderr является важным понятием, так как позволяет нам "угрожать", то есть перенаправлять каждый вывод отдельно. Нотация > используется для перенаправления stdout в файл, тогда как нотация 2> используется для перенаправления stderr, а &> используется для перенаправления как stdout, так и stderr. Команда cat используется для отображения содержимого любого файла. Рассмотрим следующий пример:

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

Быстрый совет:

Если вы не уверены в том, что ваша команда выдала stdout или stderr, попробуйте перенаправить ее вывод. Например, если вам удастся успешно перенаправить ее вывод в файл с помощью нотации 2>, это означает, что ваша команда выдала stderr. И наоборот, успешное перенаправление вывода команды с помощью нотации > говорит о том, что ваша команда произвела stdout.

Вернемся к нашему сценарию backup.sh. При выполнении нашего сценария резервного копирования вы могли заметить дополнительное сообщение, выводимое командой tar:

tar: Removing leading `/' from member names

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

Теперь, когда у нас есть базовое понимание перенаправления вывода, мы можем устранить это нежелательное сообщение stderr, перенаправив его с помощью нотации 2> в /dev/null. Представьте себе /dev/null как поглотитель данных, который отбрасывает любые данные, перенаправленные на него. Для получения дополнительной информации выполните команду man null. Ниже приведена наша новая версия backup.sh, включающая перенаправление stderr в tar:

#!/bin/bash

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

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

tar -czf $output $input 2> /dev/null
echo "Backup of $input completed! Details about the output backup file:"
ls -l $output

После выполнения новой версии нашего сценария backup.sh сообщение tar stderr отображаться не будет.

Последнее понятие, которое мы кратко рассмотрим в этом разделе, - это вход в оболочку. Помимо вышеперечисленных дескрипторов stdout и stderr в оболочке bash также имеется дескриптор ввода под названием stdin. Как правило, ввод в терминале происходит с клавиатуры. Любое нажатие клавиши, которое вы набираете, принимается как stdin.

Альтернативным методом является прием командного ввода из файла с использованием нотации <. Рассмотрим следующий пример, в котором мы сначала подаем команду cat с клавиатуры и перенаправляем вывод в файл file1.txt. Позже мы разрешаем команде cat читать ввод из файла file1.txt, используя нотацию <:

Функции в программировании Bash

Тема, которую мы собираемся обсудить далее, - это функции. Функции позволяют программисту организовать и повторно использовать код, что повышает эффективность, скорость выполнения и читабельность всего скрипта.

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

Быстрый совет:

Как только вы заметите, что ваш скрипт содержит две строки одинакового кода, вы можете подумать о том, чтобы ввести вместо них функцию.

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

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

Имя функции - user_details, а тело функции, заключенное в фигурные скобки, состоит из группы двух команд echo. Каждый раз, когда происходит вызов функции с помощью ее имени, выполняются обе команды echo в определении функции. Важно отметить, что определение функции должно предшествовать вызову функции, иначе скрипт вернет ошибку function not found:

Как показано в приведенном выше видеопримере, функция user_details группирует несколько команд в одну новую команду user_details.

В предыдущем видеопримере была представлена еще одна техника написания скриптов или любых других программ - техника отступов. Команды echo в определении функции user_details были намеренно сдвинуты на один TAB вправо, что делает наш код более читабельным и облегчает поиск неисправностей.

С отступом гораздо яснее видно, что обе команды echo находятся ниже определения функции user_details. Не существует общей конвенции о том, как делать отступы в сценариях bash, поэтому каждый человек может выбрать свой собственный способ отступа. В нашем примере использован TAB. Однако совершенно нормально вместо одного TAB использовать 4 пробела и т.д.

Имея в рукаве базовое понимание функций сценариев bash, давайте добавим новую функцию в наш существующий сценарий backup.sh. Мы запрограммируем две новые функции для сообщения о количестве каталогов и файлов, которые будут включены в выходной файл сжатого резервного копирования.

#!/bin/bash

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

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

# The function total_files reports a total number of files for a given directory. 
function total_files {
        find $1 -type f | wc -l
}

# The function total_directories reports a total number of directories
# for a given directory. 
function total_directories {
        find $1 -type d | wc -l
}

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

echo -n "Files to be included:"
total_files $input
echo -n "Directories to be included:"
total_directories $input

echo "Backup of $input completed!"

echo "Details about the output backup file:"
ls -l $output

Просмотрев приведенный выше сценарий backup.sh, вы заметите следующие изменения в коде:

  • мы определили новую функцию под названием total_files. Эта функция использует команды find и wc для определения количества файлов, расположенных в каталоге, предоставленном ей во время вызова функции.
  • мы определили новую функцию под названием total_directories. Так же, как и вышеупомянутая функция total_files, она использует команды find и wc, но при этом сообщает количество каталогов в каталоге, предоставленном ей во время вызова функции.

Быстрый совет:

Читайте страницы руководства, если хотите узнать больше о параметрах команд find, wc и echo, используемых нашим bash-скриптом backup.sh. Пример: $ man find

После того как вы обновите свой скрипт, включив в него новые функции, выполнение скрипта даст результат, аналогичный приведенному ниже:

$ ./backup.sh 
Files to be included:19
Directories to be inlcuded:2
Backup of /home/linuxconfig completed!
Details about the output backup file:
-rw-r--r-- 1 linuxconfig linuxconfig 5520 Aug 16 11:01 /tmp/linuxconfig_home_2017-08-16_110121.tar.gz

Числовые и строковые сравнения в программировании Bash

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

Рассмотрев приведенную выше таблицу, предположим, что мы хотим сравнить числовые значения, например, два целых числа 1 и 2. В следующем видео примере мы сначала определим две переменные $a и $b для хранения наших целых значений.

Затем мы используем квадратные скобки и операторы числового сравнения для выполнения фактической оценки. Используя команду echo $?, мы проверяем возвращаемое значение ранее выполненной оценки. Для каждой оценки существует два возможных результата: true или false. Если возвращаемое значение равно 0, то оценка сравнения истинна. Если же возвращаемое значение равно 1, то оценка будет ложной.

Используя операторы сравнения строк в программировании Bash, мы можем сравнивать строки так же, как и при сравнении числовых значений. Рассмотрим следующий пример:

Если бы мы перевели вышеизложенные знания в простой сценарий оболочки bash, сценарий выглядел бы так, как показано ниже. Используя оператор сравнения строк =, мы сравниваем две разные строки, чтобы определить, равны ли они.

Аналогично мы сравниваем два целых числа с помощью оператора числового сравнения, чтобы определить, равны ли они по значению. Помните, что 0 означает истину, а 1 - ложь:

#!/bin/bash

string_a="UNIX"
string_b="GNU"

echo "Are $string_a and $string_b strings equal?"
[ $string_a = $string_b ]
echo $?

num_a=100
num_b=100

echo "Is $num_a equal to $num_b ?" 
[ $num_a -eq $num_b ]
echo $?

Сохраните вышеупомянутый сценарий как, например, файл comparison.sh, сделайте его исполняемым и выполните:

$ chmod +x compare.sh 
$ ./compare.sh 
Are UNIX and GNU strings equal?
1
Is 100 equal to 100 ?
0

Быстрый совет:

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

Кроме образовательной ценности, приведенный выше сценарий не служит никакой другой цели. Операции сравнения станут более понятными, когда мы познакомимся с условными операторами типа if/else. Условные операторы будут рассмотрены в следующей главе, и именно там мы сможем более эффективно использовать операции сравнения.

Условные утверждения в программировании Bash

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

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

ЕСЛИ количество файлов в исходном и целевом каталоге одинаково, ТО выведите сообщение OK, ИЛИ - ERROR.

Давайте начнем с создания простого сценария bash, изображающего базовую конструкцию if/then/else.

#!/bin/bash

num_a=100
num_b=200

if [ $num_a -lt $num_b ]; then
    echo "$num_a is less than $num_b!"
fi

На данный момент условие else было намеренно опущено, мы включим его, когда поймем логику, лежащую в основе приведенного выше сценария. Сохраните сценарий под именем, например, if_else.sh и выполните его:

Строки 3 - 4 используются для инициализации целочисленных переменных. В строке 6 мы начинаем условный блок if. Далее мы сравниваем обе переменные, и если результат сравнения равен true, то в строке 7 команда echo сообщит нам, что значение переменной $num_a меньше по сравнению с переменной $num_b. Строка 8 закрывает наш условный блок if ключевым словом fi.

Важное наблюдение, которое можно сделать после выполнения скрипта, заключается в том, что в ситуации, когда переменная $num_a больше, чем $num_b, наш скрипт не реагирует. Вот здесь и пригодится последний кусочек головоломки - условный else. Обновите свой скрипт, добавив блок else, и выполните его:

#!/bin/bash

num_a=400
num_b=200

if [ $num_a -lt $num_b ]; then
    echo "$num_a is less than $num_b!"
else
    echo "$num_a is greater than $num_b!"
fi

Строка 8 теперь содержит часть else нашего условного блока. Если оценка сравнения в строке 6 выдает ложное значение, то выполняется код ниже оператора else, в нашем случае строка 9.

Упражнение:

Можете ли вы переписать сценарий if_else.sh, чтобы изменить логику его выполнения таким образом, чтобы блок else выполнялся, если переменная $num_a меньше переменной $num_b?

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

#!/bin/bash

user=$(whoami)
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 "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

В приведенный выше сценарий внесено несколько дополнений. Выделены наиболее важные изменения.

Строки 15 - 21 используются для определения двух новых функций, возвращающих общее количество файлов и каталогов, включенных в результирующий сжатый файл резервной копии. После выполнения резервного копирования в строке 23 в строках 25 - 29 объявляются новые переменные для хранения общего количества исходных и целевых файлов и каталогов.

Переменные, касающиеся резервного копирования файлов, в дальнейшем используются в строках 36 - 42 как часть нашего нового условного оператора if/then/else, возвращающего сообщение об успешном резервном копировании в строках 37 - 39 только в том случае, если общее количество исходных и целевых резервных файлов равно, как указано в строке 36.

Вот выполнение сценария после применения вышеуказанных изменений:

$ ./backup.sh 
Files to be included: 24
Directories to be included: 4
Files archived: 24
Directories archived: 4
Backup of /home/linuxconfig completed!
Details about the output backup file:
-rw-r--r-- 1 linuxconfig linuxconfig 235569 Sep 12 12:43 /tmp/linuxconfig_home_2017-09-12_124319.tar.gz

Позиционные параметры в программировании Bash

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

При использовании позиционных параметров bash в программировании это довольно простая задача. Позиционные параметры назначаются через аргументы командной строки и доступны в сценарии как переменные $1, $2...$N. Во время выполнения скрипта любые дополнительные элементы, заданные после имени программы, считаются аргументами и доступны во время выполнения скрипта. Рассмотрим следующий пример:

Давайте рассмотрим используемый выше сценарий примера bash более подробно:

#!/bin/bash

echo $1 $2 $4
echo $#
echo $*

В строке 3 мы выводим 1-й, 2-й и 4-й позиционные параметры точно в том порядке, в котором они заданы во время выполнения сценария. 3-й параметр доступен, но намеренно опущен в этой строке. Используя $# в строке 4, мы печатаем общее количество предоставленных аргументов. Это полезно, когда нам нужно проверить, сколько аргументов предоставил пользователь во время выполнения скрипта. Наконец, $* в строке 5 используется для печати всех аргументов.

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

#!/bin/bash                                                                                                                                                                                                                                                                    

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

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 "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

Приведенное выше обновление сценария backup.sh вводит несколько новых техник написания сценариев bash, но остальной код между строками 5 - 13 уже должен быть понятен. В строке 5 используется опция -z bash в сочетании с условным оператором if для проверки того, содержит ли позиционный параметр $1 какое-либо значение. Опция -z просто возвращает true, если длина строки, которая в нашем случае является переменной $1, равна нулю. Если это так, то мы устанавливаем переменную $user в имя текущего пользователя.

Далее в строке 8 мы проверяем, существует ли домашний каталог запрашиваемого пользователя, используя опцию -d bash. Обратите внимание на восклицательный знак перед опцией -d. Восклицательный знак в данном случае выступает в роли отрицателя. По умолчанию опция -d возвращает true, если каталог существует, следовательно, наш ! просто изменяет логику и в строке 9 выводит сообщение об ошибке. В строке 10 используется команда exit, приводящая к завершению выполнения сценария. Мы также присвоили exit значение 1, а не 0, что означает, что сценарий завершился с ошибкой. Если проверка каталога прошла, то в строке 12 мы присваиваем переменную $user позиционному параметру $1 в соответствии с запросом пользователя.

Пример выполнения сценария:

$ ./backup.sh 
Files to be included: 24
Directories to be included: 4
Files archived: 24
Directories archived: 4
Backup of /home/linuxconfig completed!
Details about the output backup file:
-rw-r--r-- 1 linuxconfig linuxconfig 235709 Sep 14 11:45 /tmp/linuxconfig_home_2017-09-14_114521.tar.gz

$ ./backup.sh abc123
Requested abc123 user home directory doesn't exist.

$ ./backup.sh 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 Sep 14 11:45 /tmp/damian_home_2017-09-14_114534.tar.gz

Быстрый совет:

Просмотрите страницу руководства bash с помощью команды $ man bash для получения дополнительной информации о -z, -d и других опциях bash. В настоящее время каталог хранения по умолчанию - /tmp. Возможно, сценарий может быть более гибким? Можете ли вы придумать, как использовать позиционный параметр $2, чтобы позволить пользователю самому решать, какой каталог использовать для хранения результирующего файла резервной копии?

Изучи программирование Bash на нашем курсе по Linux

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

Нравится программирование Bash? Продолжение следует!

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

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

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

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

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

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

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

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

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

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

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

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