Как создать файл скрипта в freebsd

Обновлено: 06.07.2024

Поговорим о создании резервной копии нашей системы на FreeBSD тобишь бэкапа.

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

Итак, ближе к делу

В этой статье я приведу один из множества вариантов организации бэкапа сервера на FreeBSD. Полностью: от создания, до восстановления.

Создание бэкапа FreeBSD

Для создания резервной копии нашей системы воспользуемся утилитой dump.

Важное замечание, необходимо чтобы в сервере помимо жесткого диска с системой был вставлен другой физический жесткий диск с примонтированной папкой, куда мы и будем делать бэкап. (Далее в примере это папка BACKUPDUMP)

Создадим файл со скриптом на shell в папке /usr/local/etc, который будет делать наш бэкап. И назовем его script_backup.sh

Разберем утилиту dump

Не забываем дать скрипту права на выполнение.

chmod 755 script_backup.sh

И положим его в cron, добавив такую строчку

0 5 10,20,30 * * root /usr/local/etc/script_backup.sh

В 5 утра примерно каждые 10 дней наш скрипт будет выполняться, обновляя файл info.txt и перезаписывая в файлы бэкапы разделов.

Восстановление из бэкапа

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

Скачиваем ее здесь и записываем образ на диск или флешку.

Грузимся с диска или с флешки и начинаем восстановление:

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

(ВАЖНО: не удалите случайно данные на жестком диске с файлами бэкапа, лучше на всякий случай на время установки отсоедините его)

Если же вы просто хотите вернуть систему к предыдущему состоянию, то соответственно разделы все уже на месте, поэтому просто восстанавливаем систему из бэкапа:

Итак, мы уже имеем разделы на жестком диске и загрузили Frenzy

Здесь приведен пример восстановления раздела usr, имена разделов будут отличаться от ваших.

Практика: sh(1), chmod(1)

Комментарий

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

7.7.1. Магическая строка (shebang)

При попытке выполнить скрипт мы получим:

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

7.7.2. Почему sh(1) ?

Надо честно признать, повседневно работать в sh(1) неудобно. Да, sh(1) неудобный интерпретатор, ему есть множество альтернатив, которые, к тому же, порой полностью совместимы по синтаксису. Почему же в операционных системах BSD так упорно цепляются за него? Для начала приведу выдержку из FreeBSD FAQ .

Сравним работу двух одинаковых с виду программ на sh :

и на bash :

Как видите, эти программы отличаются только первой строчкой. В этих программах осуществляется в цикле тысячекратный вызов программы bc(1) , крохотного калькулятора, для увеличения счётчика. И столько же раз вызвана команда test(1) (см. ниже).

Легко видеть, что тяжеловесный bash(1) ворочал этот скрипт почти вдвое дольше. На разных опробованных мною системах соотношение времени выполнения этого скрипта bash / sh колебалось от 1.3 до 1.9.

7.7.3. Программирование в Bourne Shell

7.7.3.1. Синтаксис

Таблица 7.7. Синтаксическая таблица Bourne Shell

7.7.3.2. Работа с переменными в sh(1)

Присваивание переменной осуществляется при помощи оператора = . Вызов определённой ранее переменной, при помощи префикса $ . Например:

Следует подчеркнуть: переменные и переменные окружения это не одно и то же. Любая переменная окружения видна как переменная, но не любая переменная видна как переменная окружения. Чтобы переменная стала переменной окружения её надо экспортировать:

Таблица 7.8. Специальные переменные в Bourne Shell

7.7.3.3. Условные операторы

В качестве условия в sh(1) выполняется некоторая команда, и изучается её код возврата. Если код возврата равен нулю, и, следовательно, программа завершилась успешно, sh(1) трактует это как истину, если программа вернула код возврата больше нуля, sh(1) трактует это как ложь.

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

Такой синтаксис краток, но не следует им злоупотреблять. В конечном счёте это ведёт к трудно читаемым программам. Ту же проверку лучше осуществить с использованием явного оператора if :

Если эти операторы понадобится написать в одну строку, то надо понимать, что ключевые слова if , then , else и fi , это самостоятельные команды и они в строке они должны предваряться знаком ; .

Ветвь else , разумеется, необязательна. Если нам надо проверить несколько альтернатив, то, чтобы не вкладывать много условных операторов друг в друга, мы можем использовать оператор elif .

Недостатком данной конструкции является то, что здесь шесть раз вызываются программы uname(1) и grep(1) . Существует более очевидная конструкция для проверки на соответвие строки списку значений.

Специально для условного оператора sh(1) существует программа осуществляющая математические проверки, проверки на существование файловых объектов и равенство строк. В зависимости от результата сравнения эта программа возвращает либо ноль, либо единицу. Речь идёт о программе test(1) .

Таблица 7.9. Опции команды test(1)

Для команды test(1) существует альтернативное имя [ . Если она вызывается по имени [ , то она разбирает командную строку вплоть до того, пока не встретит закрывающую квадратную скобку. Таким образом, следующие четыре конструкции эквивалентны:

Обратите внимание: вокруг квадратных скобок обязательно должны быть пробелы, потому что [ это не синтаксическая конструкция sh(1) , а обычная команда на подобии test(1) .

7.7.3.4. Циклы

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

Следующая программа выводит список квадратов натуральных чисел от 1 до 100, используя цикл с условием while :

Ключевые слова do и done ограничивают тело цикла, при написании в одной строке они должны предваряться ; .

Цикл может быть прерван встроенной командой break :

Встроенная команда continue предназначена для прерывания текущей итерации. Т.е. выполнение цикла будет продолжено, но текущая итерация будет прервана. Если после команд break или continue указано число, то оно означает глубину цикла, который будет ими прерван.

Следующая программа конвертирует все картинки в каталоге image из формата GIF в формат PNG

Конструкция image/*.jpg превратится в список файлов в каталоге image имя которых оканчивается на .jpg .

Первая проверка нужна для того, чтобы убедиться, что мы имеем дело с файлом в формате JPEG . Вдруг файл имеющий это расширение на самом деле никакой не JPEG , а, скажем MP3 ? В случае, если это не JPEG мы используем прерывание текущей итерации (но не всего цикла целиком) при помощи команды continue .

Обратите внимание на использование кавычек: внутри двойных кавычек переменные раскрываются в свои значения. Кавычки мы использовали для того, чтобы в одном случае строка трактовалась бы утилитой grep(1) как один аргумент, несмотря на наличие в ней пробелов, в другом случае, чтобы защитить символ > и в третьем случае защитить одинарную кавычку.

Конструкция $ указывает sh(1) , что надо взять значение переменной $filename и отрезать с конца фрагмент jpg .

7.7.3.5. Функции

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

При помощи директивы return функция может вернуть свой собственный код возврата.

В sh(1) существует конструкция, позволяющая подгружать внешние файлы с определёнными в них функциями и переменными. Допустим у нас есть файл math в котором имеются следующие определения:

Для начала, создадим в домашней директории тестовый Bash-скрипт myscript.sh с содержанием

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

chmod 777 myscript.sh

./myscript.sh

./myscript.sh: Command not found.

Устанавливаем Bash

cd /usr/ports/shells/bash

make install clean

ln -s /usr/local/bin/bash /bin/bash

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

./myscript.sh

The current directory is:

/home/user

The user logged in is:

user

После этого Вы можете выполнять Bash-скрипты или установить bash, как оболочку по умолчанию, для этого нужно выполнить

Если Вы планируете использовать Bash, как командную оболочку, рекомендую установить пакет bash-completion, он позволит по нажатию TAB видеть возможные окончания команд

cd /usr/ports/shells/bash-completion/

make install clean

To enable the bash completion library, add the following to

your .bashrc file:

[[ $PS1 && -f /usr/local/share/bash-completion/bash_completion.sh ]] &&

source /usr/local/share/bash-completion/bash_completion.sh

See /usr/local/share/doc/bash-completion/README.md for more information.

Оно означает, что в папке пользователя нужно создать файл .bashrc с содержанием

[[ $PS1 && -f /usr/local/share/bash-completion/bash_completion.sh ]] && source /usr/local/share/bash-completion/bash_completion.sh

Послесловие.

В целом, без Bash можно жить долго или он может никогда Вам не понадобиться, так как для ряда задач будет достаточно и sh, но за время работы я встречался с тем, что ряд решений легче написать или найти написанными для Bash. Также встречался ряд программных продуктов, которые прекрасно работали на FreeBSD, но во время установки, настройки или работы выполняли ряд Bash-скриптов (например, Sync-сервера или плагины для Nagios)

Знакомство с системой зачастую начинается с ее установки и настройки, причем эти два процесса обычно совмещены. Для систем UNIX настройка сводится к созданию текстовых файлов конфигурации. В FreeBSD основная работа идет над файлом /etc/rc.conf, который является частью системы стартовых скриптов /etc/rc*.

Настроить систему после установки можно путем запуска /stand/sysinstall или вручную. Оба способа хороши, а первый к тому же лишен недостатка большинства программ автоматической настройки: уже заданные параметры не перезаписываются, а добавляются в конец файла /etc/rc.conf, где их можно затем отредактировать под собственные нужды или вообще удалить, чтобы вернуться к настройкам по умолчанию.

Многие, казалось бы, сложные вещи, которые могут поставить новичка в тупик, в FreeBSD делаются путем добавления пары строк в этот файл. Чтобы не быть голословным, приведу пример настройки шлюза в Интернет для локальной сети с частными адресами (192.168.0.0/24) на внутреннем интерфейсе и NAT на внешнем, кэширующим DNS-сервером и SSH для удаленного управления всем этим хозяйством. Хотя это не самый простой пример, конфигурация умещается всего на 11 строчках:

ifconfig_rl0="inet 123.123.123.2 netmask 0xfffffff0"

ifconfig_rl1="inet 192.168.0.1 netmask 0xffffff00"

named_enable article">Такая лаконичность достигается благодаря тому, что параметры по умолчанию хранятся в /etc/defaults/rc.conf, а строки из /etc/rc.conf лишь изменяют и дополняют эти параметры. Редактировать файл с параметрами по умолчанию не нужно (это только усложнит последующие обновления), но вот просмотреть его хотя бы раз будет полезно.

Строки rc.conf означают не что иное, как установку переменных Bourne shell (/bin/sh). Когда в процессе загрузки системы init запускает скрипт /etc/rc, одним из первых его действий становится установка параметров настройки:

if [ -r /etc/defaults/rc.conf ]; then

elif [ -r /etc/rc.conf ]; then

В данном случае перезаписыванием параметров по умолчанию занимается функция source_rc_confs (расположенная в /etc/defaults/rc.conf), которая и включает /etc/rc.conf, /etc/rc.conf.local или любые другие файлы из списка, определенного в переменной rc_conf_files. Конструкция эта могла бы быть несколько проще, но используемая функция уже содержит «защиту от дурака», поскольку некоторые пользователи считают удобным копировать /etc/defaults/rc.conf в /etc/rc.conf, что без такой защиты привело бы к зацикливанию.

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

Начиная с FreeBSD 5.x, содержимое это поменялось почти полностью: из NetBSD были заимствованы скрипты «нового поколения» /etc/rc.d. После некоторой паузы, потребовавшейся на тестирование новых скриптов, старые были убраны. Наиболее важные из оставшихся в /etc скриптов и файлы конфигурации:

  • rc – запускается init при загрузке системы.
  • rc.shutdown – запускается init при останове системы.
  • rc.subr – часть системы, импортированной из NetBSD.
  • rc.firewall – предназначен для настройки ipfw и natd.
  • rc.conf – содержит настройки системы, переписывающие параметры по умолчанию.
  • rc.conf.local – специфичные для хоста настройки. Если используется этот файл, rc.conf может быть общим для группы хостов.
  • rc.local – все еще поддерживается для запуска локальных сервисов. Вместо него рекомендуется использовать скрипты /usr/local/etc/rc.d.

Для редактирования предназначены только скрипт rc.firewall и файлы конфигурации. Ситуация, в которой потребовалось бы приспосабливать к своей системе остальные скрипты, возникнуть не должна (по крайней мере, теоретически). Если она все же возникла – сообщите об этом разработчикам.

Принципиальное отличие скриптов нового поколения – поддержка параметров запуска. По сравнению со скриптами из /usr/local/etc/rc.d список этих параметров существенно расширен. К минимальному набору (start|stop) добавились:

  • restart – последовательно stop и start.
  • reload – перегружает процесс, посылая ему сигнал.
  • status – показывает статус процесса.
  • poll – ждет завершения процесса.
  • rcvar – показывает соответствующие переменные rc.conf.

Параметры status, reload и poll реализуются только в том случае, если скрипт запускает какой-либо процесс.

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

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

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

К тому же запуск вручную не всегда тривиален: к примеру, для запуска NFS сервера в FreeBSD 4.x так, как это происходит при установке параметра nfs_server_enable=«YES» в rc.conf, потребуются следующие команды:

В FreeBSD 5.x достаточно добавить эту переменную и выполнить команду:

Очевидно, это гораздо проще и к тому же проверена на правильность соответствующая переменная. В примере с NFS в обеих случаях сначала необходимо подготовить файл /etc/exports (man exports(5)).

Другие параметры также достаточно интересны: например, status поможет узнать, какие процессы запущены в данный момент, а rcvar – познакомиться со скриптами поближе:

Для стартовых скриптов системы, в отличие от локальных скриптов в /usr/local/etc/rc.d, очень важен порядок запуска. Локальные скрипты не имеют механизма реализации зависимостей и определения порядка запуска. Они стартуют в алфавитном порядке и если необходимо, чтобы какой-то скрипт стартовал раньше, нужно «повысить» его место по алфавиту. Обычно перед именем скрипта добавляют пару-тройку нулей.

Зависимости и определение порядка запуска в системных стартовых скриптах FreeBSD 5.x реализуются с помощью rcorder(8).

Эта программа использует специальные ключевые слова, содержащиеся в каждом скрипте: REQUIRE, PROVIDE, BEFORE, KEYWORD. Для оболочки строки, содержащие эти слова, являются комментариями. Вот пример блока ключевых слов /etc/rc.d/nfsd (все пробелы обязательны):

Этот скрипт предоставляет сервис nfsd, требует предварительного запуска mountd и работает в FreeBSD и NetBSD.

Должен быть хотя бы один скрипт без ключевого слова REQUIRE, чтобы rcorder мог определить первый стартовый скрипт. Например, /etc/rc.d/devd содержит следующий блок:

В FreeBSD правильную последовательность скриптов выдает следующая команда:

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

Таких скриптов всего четыре: NETWORKING, SERVERS, DAEMON, LOGIN (man rc(8)). Они используются только rcorder, для sh это всего лишь набор комментариев. Остальные скрипты делятся по признаку наличия или отсутствия расширения «.sh». Если расширение присутствует, скрипт запускается в текущей оболочке.

В локальных скриптах поддержка параметров реализуется с помощью конструкции case. Параметр start предполагает запуск программы, stop – отправку ему сигнала TERM. Любой другой параметр – вывод краткой справки по параметрам. Шаблон локального скрипта выглядит так (man rc(8)):

/usr/local/sbin/prog -d && echo -n " prog"

kill `cat /var/run/prog.pid` && echo -n " prog"

echo "unknown option: $1 - should be "start" or "stop"" >&2

Реализация таким методом большего числа параметров потребует соответствующего числа ключевых слов в case и множества довольно тривиального и одинакового для всех скриптов кода (более сложного, чем в приведенном примере). Видимо, разработчикам NetBSD такое решение изящным не показалось и вся функциональность стартовых скриптов нового поколения была реализована в отдельном скрипте (/etc/rc.subr). Вот шаблон простейшего скрипта «нового поколения».

Функционально этот скрипт превосходит предыдущий за счет добавления новых параметров (и к тому же, благодаря параметрам rcorder, он «знает свое место»). Визуально он делится на четыре части: ключевые слова, включение /etc/rc.subr, переменные и запуск функций из включенного rc.subr. Между переменными и запуском функций rc.subr могут быть включены собственные функции скрипта. В скрипт могут быть помещены переменные argument_cmd. Слово argument означает параметр запуска. Если запустить любой из стартовых скриптов без параметров, он выдаст информацию примерно в таком виде:

Параметрами скрипта могут быть start, faststart, forcestart, stop, faststop. и соответственно переменная argument_cmd на самом деле означает набор start_cmd, stop_cmd.

Получить примерное представление о том, какие на самом деле используются переменные, поможет следующая команда:

$ cat /etc/rc.d/[a-z]* | egrep "^[a-z]+_?[a-z]+=.*$" | awk -F= "" | sort -u | less

Не менее интересен может быть просмотр переменных вместе с их значениями:

$ cat /etc/rc.d/[a-z]* | egrep "^[a - z]+_?[a-z]+=.*$" | sort -u | less

В man rc.subr(8) дано описание всех используемых функций и переменных.

Задаваемые в стартовых скриптах переменные используются функцией run_rc_command. Вот список этих переменных в сокращенном виде:

  • name – имя скрипта. Указывается в обязательном порядке.
  • rcvar – значение переменной, имя которой помещено в rcvar, берется из rc.conf и проверяется с помощью checkyesno. Истинными считаются значения «YES», «TRUE», «ON» или «1». Ложными «NO», «FALSE», «OFF» или «0».
  • command – полный путь к команде. Переменная не нужна, если для каждого поддерживаемого параметра указана переменная argument_cmd.
  • command_args – опциональные аргументы и/или директивы оболочки.
  • command_interpreter – командный интерпретатор, с помощью которого запускается command.
  • extra_commands – дополнительные параметры запуска.
  • pidfile – полный путь к файлу PID. Если эта переменная установлена, для проверки процесса используется check_pidfile $pidfile $procname. Иначе, если установлена переменная command, используется check_process $procname.
  • procname – имя процесса для проверки. По умолчанию – procname.
  • required_dirs – проверка на наличие директории перед стартом программы.
  • required_files – проверка на читаемость файлов.
  • required_vars – проверка переменной с помощью checkyesno.
  • $_chdir – каталог, в который нужно войти перед запуском command, если нет – $_chroot.
  • $_chroot – каталог для запуска chroot(8) перед запуском command.
  • $_flags – эта переменная используется для перезаписи соответствующей переменной из rc.conf.
  • $_nice – устанавливает уровень nice(1) перед запуском command.
  • $_user – пользователь, под которым запускается command. Если установлена переменная $_chroot, используется chroot(8), иначе – su(1).
  • $_group – группа для запуска command в chroot.
  • $_groups – разделенный запятыми список групп для запуска в chroot.
  • argument_cmd – перезаписывает действие по умолчанию.
  • argument_precmd – выполняется перед argument_cmd или действием по умолчанию, которое выполняется только в случае успешного завершения argument_precmd.
  • argument_postcmd – выполняется, если argument_cmd или действие по умолчанию успешно выполнены.
  • sig_stop – сигнал, который посылается процессу для его завершения вместо SIGTERM.
  • sig_reload – сигнал, который посылается процессу для перезагрузки вместо SIGHUP.

Префикс fast означает отсутствие проверки запущенных процессов перед запуском нового и установку переменной rc_fast=YES, а префикс force – установку переменной rc_force=YES и игнорирование статуса завершения argument_precmd.

Читайте также: