Shell programming.

Опубликовано beregov.a.e - сб, 03/12/2022 - 21:55

Command: date.

Благодарю что обратили внимание на эту статью. Надеюсь она будет вам полезна. Дочитав это вы узнаете, как работать с датой на unix системах. Узнаете, как парсить строку с датой. И выполнять такие операции: Сложение, вычитание и сравнение. Если вы начинаете программировать в shell и мало опыта, то это вам сильно поможет, так как исходя из моего опыта в программировании-это один из основных моментов в программировании в shell, для написания скриптов по администрированию серверов. На это, почему-то, мало обращают внимание. Буду демонстрировать в окружениях GNU Linux и BSD FreeBSD. Так как между ними есть отличия. Когда будут отличия, я отдельно буду это разделять.

Почему работа с датой это так важно? Вот несколько примеров того, где это нужно:

-Резервное копирование как файловое, так и выгрузка дампов баз данных.

-Настройка ротации архивов.

-Архивирование неиспользуемых или редко используемых данных.

-Перенос данных. Например с ssd на hdd или в сетевое хранилище по истечению определённого времени.

-Поиск или исключения из поиска файлов или информации в файлах, например grep файлы логов.

В зависимости от настроек локали, вывод может отличатся. Приступим к практике. Введём команду date:

FreeBSD:

$ date

Wed Feb 16 17:00:41 +05 2022

ubuntu:

$ date

Ср 16 фев 2022 17:01:59 +05

Есть документация которая поставляется с системой. Чтобы её почитать введите

$ man date

Настройка формата вывода.

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

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

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

Каталог ГОД, каталог МЕСЯЦ и каталог ДЕНЬ.

$ date "+%Y/%m/%d"

2022/02/16

В таком формате можно уже использовать в других программах. Например, так можно создать каталог для резервного копирования.

$ mkdir -p $(date "+%Y/%m/%d")

$ ls -l 2022/02/16/

total 0

В зависимости от стратегии резервного копирования, я сохраняю дампы баз данных за день в одном каталоге. И удаляю старые копии по истечению определённого времени. Для файлов удобней использовать другой формат. Например:

$ date "+%Y-%m-%d"

2022-02-16

или так

$ date "+%Y-%m-%d_%H-%M"

2022-02-16_17-25

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

На практике это выглядит примерно так:

$ pg_dump site > site_$(date "+%Y-%m-%d_%H-%M").sql

В моём случаи получилось файл с дампом базы. В текущем каталоге откуда запускал выгрузку.

site_2022-02-16_17-28.sql

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

На один день.

Ubuntu:

$ date -d "-1 day" "+%Y-%m-%d"

2022-02-15

FreeBSD

$ date -v-1d +%Y-%m-%d

2022-02-15

На 5 дней.

На зад.

Ubuntu:

$ date -d "-5 day" "+%Y-%m-%d"

2022-02-10

В перед:

$ date -d "+5 day" "+%Y-%m-%d"

2022-02-20

 

На зад:

FreeBSD

$ date -v-5d +%Y-%m-%d

2022-02-10

В перед:

$ date -v +5d +%Y-%m-%d

2022-02-20

Месяц.

ubuntu

На зад:

date -d "-1 month" "+%Y-%m-%d"

2022-01-15

В перед:

date -d "+1 month" "+%Y-%m-%d"

2022-03-15

FreeBSD

На зад:

$ date -v-1m +%Y-%m-%d

2022-01-15

В перед:

$ date -v +1m +%Y-%m-%d

2022-03-15

На год .

ubuntu

На зад:

date -d "-1 year" "+%Y-%m-%d"

2021-02-17

В перед:

date -d "+1 year" "+%Y-%m-%d"

2023-02-17

FreeBSD

На зад:

$ date -v-1y +%Y-%m-%d

В перед:

2021-02-16

$ date -v +1y +%Y-%m-%d

2023-02-16

Добавим и удалим произвольную дату к текущей дате.

Ubuntu

$ date -d "-1 year -1 day"

Вт 15 фев 2021 14:09:20 +05

$ date -d "1 year 1 day"

Сб 17 фев 2023 14:10:23 +05

FreeBSD

$ date -v-1y -v+1H

среда, 16 февраля 2021 г. 15:13:25 (+05)

date -v+1y -v+1w

среда, 23 февраля 2023 г. 14:21:20 (+05)

 

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

 

Сравнение даты и времени.

И так, сравнить две строки как-то сложно, если только они не одинаковы. Что же тогда делать? По идее, сравнить можно только числа. Дата и время это вроде как-то не число. Или число? Если вы уже продвинуты в программировании, то знаете, что отчёт времени идёт от старта эпохи unix. На unix подобных операционных. И это значение измеряется в секундах. Например, текущее время в секундах на момент выполнения команды

 

$ date "+ %Y.%m.%d - %H:%M:%S - в секундах %s"

2022.02.17 - 16:07:01 - в секундах 1645096021

$ date "+%s"

1645096046

 

Вот секунды можно уже сравнивать. Но как тогда привести строку с датой в секунды. К счастью, разработчики уже подумали. Это называется парсинг. Для тех, кто пользуется linux дистрибутивами, разработчики очень хорошо подумали над этим. Для BSD тоже подумали, по своему:

ubuntu

$ date -d "2022-02-15 11:02:31 +0500" +%s

1644904951

$ date -d "2032-05-13" +'%s'

1968001200

freebsd

$ date -jf "%Y-%m-%d" "2032-05-13" +'%s'

1968063987

$ date -jf "%F %T %z" "2022-02-15 11:02:31 +0500" +'%s'

1644904951

Как видно, парсинг строки отличается в разных операционных системах. Во FreeBSD нужно задать формат строки. В ubuntu автоматически парсится. Получив обычные числа, вы сможете их сравнивать. О том, как работает сравнение в bash- это не ко мне. В интернете полно хорошей документации с примерами.

И так, вы уже научились задавать формат вывода даты. Также, умеете путешествовать по времени. И даже сможете сравнить две даты между собой. Теперь преминем это на практике.

Сколько времени осталось до нужно даты или сколько прошло.

С начала года

date +%j  дней
date +%W недель
 Работа с произвольной датой.
	Вводная. Работать будем с секундой. В одной минуте - 60 секунд. В часе - 3600. В сутках - 86400.
Сколько дней осталось до произвольной даты, начиная с текущего момента? Ранее вы уже узнали, как парсить дату и изменять формат вывода.  Итак, один из способов - это перевести всё в секунды, а далее выполнить арифметические операции.  Для этого, буду использовать команду expr. Возьмём текущие дату и время и 2022-02-15. Узнаем сколько  времени прошло с того момента, как я начал писать данную заметку.   
Ubuntu:
$ expr $(expr $(date +%s) - $(date -d "2022-02-15" +%s)) / 86400
$ 25
или можно по другому записать:
expr $(( $(date +%s) - $(date -d "2022-02-15" +%s))) / 86400

Freebsd:

expr $(($(date +%s ) - $(date -jf "%Y-%m-%d" "2022-02-15" +%s ))) / 86400

$ 25
25 дней прошло. Много наверное для кого-то. Теперь возьмём две даты. 2022-02-15 и 2000-02-15 и узнаем сколько дней в этом промежутке. 
Ubuntu:
$ expr $(expr $(date -d "2022-02-15" +%s) - $(date -d "2000-02-15" +%s)) / 86400
$ 8036

Freebsd:

expr $(($(date -jf "%Y-%m-%d" "2022-02-15" +%s ) - $(date -jf "%Y-%m-%d" "2000-02-15" +%s ))) / 86400
8036

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

 

Практика.

Нужен скрипт для резервного копирования баз данных кластера. Ротация дампов баз данных должна выполнятся по следующей схеме.

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

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

Скрипт не претендует на то, что так, и только так надо делать. Это только как один из вариантов и не более. Проверка на то, что дата в порядке, появилась из-за того, что я столкнулся с данной ситуацией. Бэкапы потёрлись. И, к счастью, они не понадобились. Можно, ещё для надёжности, поставить флаг не изменяемости на те дампы, которые на начало месяца. Но лучше отправить сообщение на почту или настроить мониторинг. Что выходит за рамки данной статьи. Теперь сам скрипт:

 

#!/bin/bash

year=`date +%Y` # Текущий год для проверки что с датой всё в порядке.

deys=`for (( i=0; i<=90 ; i++ )) ; do date -v-${i}d +%Y-%m-%d ; done`

#

# Это генерация массива дат которые нужно оставить.

# можно было бы через find. Но тогда как найти на первое число

# каждого месяца. Если такой задачи нет то

# можно воспользоваться данной командой.

#

months=`for (( i=0; i<=144 ; i++ )) ; do date -v-${i}m +%Y-%m-01 ; done`

save_db=`echo ${deys} ${months} | sed 's![ \t]!\\\|!g'`

 

# замена пробельных символов на \| для дальнейшего

# использования в grep

 

databases=(base base3)

 

# Массив баз для бэкапа. Удобно.

 

pg_databases=( site site_cloud)

TMP_SQL=/tmp/ #каталог для первичной выгрузки дампа

BACKUPDIR=/storage/db_every_day/ #каталог где хранятся дампы

DATE_FILE=`date "+%Y-%m-%d"` # текущая дата в удобном формате для файла

host_pgsql=192.168.0.200 #

hosq_mysql=192.168.0.210 #

 

#ip сервера баз данных с которого требуется получить дамп.

 

rm_files () {

find ${BACKUPDIR} -type f | grep -v $save_db | xargs rm -f

}

 

# функция удаления устаревших дампов баз данных

 

backup_mysql () {

for db in "${databases[@]}"

do

FILE=${TMP_SQL}`date "+%Y-%m-%d"`_${db}

mysqldump --routines --single-transaction --no-autocommit -h{hosq_mysql} ${db} -uroot > ${FILE}.sql

xz -v -9 --threads=8 ${FILE}.sql

mv ${FILE}.sql.xz ${BACKUPDIR}

done

 

#

# функция создания дампов баз данных которые работают на mysql

#

 

backup_pgsql () {

for db in ${pg_databases[@]}

do

FILE=${TMP_SQL}`date "+%Y-%m-%d"`_${db}

pg_dump -h${host_pgsql} -Upostgres ${db} > ${FILE}.pg.sql

xz -v -9 --threads=0 ${FILE}.pg.sql

mv ${FILE}.pg.sql.xz ${BACKUPDIR}

done

}

 

#

# функция создания дампов баз данных которые работают на pgsql

#

 

main ()

{

if [ $year -ge 2018 ]; # Проверяем какой сегодня год. Если больше 2018 или равно то выполняем создание резервной копии базы

then

backup_mysql

backup_pgsql

rm_files

fi

}

 

main

 

Теперь задание вам.

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

Исходя из размера последних дампов баз, можно добавить проверки на наличие каталога и свободного места, чтобы места хватило ещё на 7 дней.

 

 

Теги