Обновление скрипта резервного копирования

Обновил скрипт для резервного копирования. Убрал вызов bc, вынес всё в отдельные функции, причесал код, чтобы ShellCheck был счастлив:

#!/usr/local/bin/bash
 
BACKUP_ROOT=/var/backups/manual/
BACKUP_DIR="$BACKUP_ROOT"$(date +"%Y-%m-%d/%H-%M")
PREFIX=$(date +"%Y%m%d_%H%M")
LIMIT=$((4*1024*1024))
 
exit_on_error() {
    exit_code=$?
    if [ $exit_code -ne 0 ]; then
        >&2 echo -e "FAIL"
        exit $exit_code
    fi
}
 
print_used_space() {
    printf "Used space: "
    SIZE=$(du -sB 1 $BACKUP_ROOT | cut -f 1)
    printf "%sMb(%s%%)\n" "$((SIZE / 1024))" "$((SIZE * 100 / LIMIT))"
}
 
backup_etc() {
    FILENAME="$PREFIX"_etc.tar.bz2
    printf "Creating %s .. " "$FILENAME"
    if /usr/bin/tar -Pjcf "$BACKUP_DIR"/"$FILENAME" /etc /usr/local/etc /home /var/db/ports /var/named/etc/namedb /root/scripts > /dev/null 2>&1; then
        printf "OK\n"
    else
        exit_on_error
    fi
}
 
backup_site() {
    FILENAME="$PREFIX"_www.tar.bz2
    printf "Creating %s .. " "$FILENAME"
    if /usr/bin/tar -Pjcf "$BACKUP_DIR"/"$FILENAME" /usr/local/www > /dev/null; then
        printf "OK\n"
    else
        exit_on_error
    fi
}
 
backup_db() {
    FILENAME="$PREFIX"_db.sql.bz2
    printf "Creating %s .. " "$FILENAME"
    if /usr/local/bin/mysqldump --login-path=backup --opt --all-databases --triggers --routines --events | bzip2 -c > "$BACKUP_DIR"/"$FILENAME"; then
        printf "OK\n"
    else
        exit_on_error
    fi
}
 
remove_old_files() {
    removed=0
    for days in $(seq 100 -1 0)
    do
        SIZE=$(du -sB 1 $BACKUP_ROOT | cut -f 1)
        if [[ $SIZE -le $LIMIT ]]; then
            break
        fi
        if [[ -z "$(find $BACKUP_ROOT -type f -mtime +"$days")" ]]; then
            continue
        fi
        printf "Removing files older than %d days:\n" "$days"
        find $BACKUP_ROOT -type f -mtime +"$days"d -delete -print
        find $BACKUP_ROOT -empty -type d -delete -print
        removed=1
    done
 
    if [ $removed -ne 0 ]; then
        true
        return
    fi
 
    false
}
 
if [ ! -d "$BACKUP_DIR" ]; then
        mkdir -p "$BACKUP_DIR"
fi
 
backup_etc
backup_site
backup_db
print_used_space
remove_old_files && print_used_space
true
Краткая инструкция по обновлению порта для сопровождающих

Примерно с 2008 года совершенно случайно являюсь сопровождающим порта sysutils/archivemount в ОС FreeBSD и недавно стал получать сообщения от их багтрекера. Их Bugzilla с упорством достойным поощрения настойчиво раз в неделю присылала мне письма об ошибках. Так что ничего не оставалось делать, как обновить соответствующий порт и в результате родилась эта инструкция.

# Делаем копию оригинала
cp -r /usr/ports/sysutils/archivemount/ /usr/ports/sysutils/archivemount.bak
# Обновляем Makefile (PORTVERSION=new_version, PORTREVISION=0)
vim /usr/ports/sysutils/archivemount/Makefile
# Обновляем файл distinfo командой make makesum
cd /usr/ports/sysutils/archivemount && make makesum
# Генерируем diff (рекомендуется использовать git diff)
diff -ruN /usr/ports/sysutils/archivemount.bak /usr/ports/sysutils/archivemount > archivemount.diff

Заливаем этот diff через форму багтрекера с припиской в начале [maintainer update]. В качестве примера можно посмотреть обновление archivemount. Также приложите CHANGELOG со списком изменений и, разумеется, проверьте работу порта и новой версии приложения перед отправкой.
Что ещё почитать:
* FreeBSD Porters Handbook
* Руководство FreeBSD по созданию портов

Скрипт для резервного копирования

Всем привет, обновил скрипт для резервного копирования с учётом того, что я вообще-то программист, а не просто айтишник! Он стал более интеллектуально определять и удалять старые файлы, учитывать ограничение в 4.5Gb (на FTP для бекапа доступно 5Gb) и успешность операций, добавлены сообщения об ошибках. И бонусом сообщения раскрашены в разные цвета, нейтральные выделены жирным, хорошие зелёным, а плохие красным цветом — красота! Скрипт проверен на сайте ShellCheck, который я настоятельно рекомендую к использованию. Если найдёте ошибку или вам есть что добавить, смело пишите комментарий.

#!/usr/local/bin/bash
 
# Основная папка для архивов
BACKUP_ROOT=/var/backups/manual/
# Папка, куда непоредственно пишем файлы, выглядит как BACKUP_ROOT/ГОД/МЕСЯЦ/ДЕНЬ
BACKUP_DIR="$BACKUP_ROOT"$(date +"%Y/%m/%d")
# Время запуска скрипта. Все файлы имеют вид имя_файла_%ЧАС%%МИНУТА%.tag.bz2
TIME=$(date +"%H%M")
# Ограничение хранилища в килобайтах для проверки
LIMIT=$(echo "4096*2^10"|bc)
# Ограничение хранилища в мегабайтах для вывода в сообщениях
HLIMIT=$(echo "$LIMIT/1024"|bc)"Mb"
 
# Функция выход в случае ошибки
exit_on_error() {
    # Получаем код ошибки
    exit_code=$?
    # Входное сообщение
    message=$1
    if [ $exit_code -ne 0 ]; then
        # Выводим сообщение в stderr
        >&2 echo -e "\e[31m${message}\e[0m"
        # Выходим к кодом exit_code
        exit $exit_code
    fi
}
 
# Если папки нет
if [ ! -d "$BACKUP_DIR" ]; then
    # Создаём папку со всеми промежуточными подпапками
    mkdir -p "$BACKUP_DIR"
fi
 
printf "\e[1mSearching space for backup\e[0m .. "
# Цикл от 100 дней до нуля
for days in $(seq 100 -1 0)
do
    SIZE=$(du -sB 1 $BACKUP_ROOT | cut -f 1)
    HSZ=$(echo "$SIZE/1024"|bc)"Mb"
    # Если размер хранилища не превышает лимита
    if [[ $SIZE -le $LIMIT ]]; then
        # Выводим сообщение и прерываем цикл
        printf "[%s/%s] .. " "$HSZ" "$HLIMIT"
        break
    fi
    # Если нет ни одного файла старше $days дней
    if [[ -z "$(find $BACKUP_ROOT -type f -mtime +"$days")" ]]; then
        # Продолжаем цикл
        continue
    fi
    printf "\e[31m%s/%s]\e[0m .. " "$HSZ" "$HLIMIT"
    # Удаление файлов старше $days прошло успешно
    if find $BACKUP_ROOT -type f -mtime +"$days"d -delete > /dev/null; then
        # Выводим сообщение 
        printf "CLEAN (older than %s days) .. " "$days"
    else
        # Выходим с сообщением FAIL
        exit_on_error "FAIL"
    fi
    # Удаляем пустые папки, чтоб не мусорить
    find $BACKUP_ROOT -empty -type d -delete
done
echo -e "\e[32mOK\e[0m"
 
printf "\e[1mBackuping etc files\e[0m .. "
/usr/bin/tar -Pjcf "$BACKUP_DIR"/etc_"$TIME".tar.bz2 /etc /usr/local/etc /home /var/db/ports /var/named/etc/namedb /root/scripts > /dev/null 2>&1 && echo -e "\e[32mOK\e[0m" || exit_on_error "FAIL"
 
printf "\e[1mBackuping www folder\e[0m .. "
/usr/bin/tar -Pjcf "$BACKUP_DIR"/www_"$TIME".tar.bz2 /usr/local/www > /dev/null && echo -e "\e[32mOK\e[0m" || exit_on_error "FAIL"
 
printf "\e[1mBackuping SQL databases\e[0m .. "
/usr/local/bin/mysqldump --login-path=backup --opt --all-databases --triggers --routines --events | bzip2 -c > "$BACKUP_DIR"/sql_"$TIME".sql.bz2 && echo -e "\e[32mOK\e[0m" || exit_on_error "FAIL"

Откровенно говоря, не до конца понимаю, в каких единицах считает занятое место команда du. Предполагаю, что в килобайтах.

P.S. Для того, чтобы получить вывод без цвета, использую утилиту textproc/ansifilter:

script.sh | ansifilter --text
Скрипт для резервного копирования mercurial, subversion, mysql и etc

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

#!/bin/sh
 
# Текущая дата
DATE=`date "+%Y-%m-%d"`
# Папка для бекапов
BACKUP_DIR=/var/backups/manual
 
# Удаляем все файлы старше семи дней
find $BACKUP_DIR -type f -mtime +7 -delete
 
# Архивируем папки /etc, /usr/local/etc и /var/db/ports
/usr/bin/tar -Pzcf $BACKUP_DIR/etc_$DATE.tar.gz /etc /usr/local/etc /var/db/ports /var/named/etc/namedb > /dev/null
# Архивируем вебсайт
/usr/bin/tar -Pzcf $BACKUP_DIR/www_$DATE.tar.gz /usr/local/www > /dev/null
# Архивируем базы данных
/usr/local/bin/mysqldump --opt -Aau backup -pbackuppass | gzip -c > $BACKUP_DIR/sql_$DATE.sql.gz
# Архивируем subversion
/usr/local/bin/svnadmin dump -q /home/svn | gzip -c > $BACKUP_DIR/svn_$DATE.gz
 
# Для всех папок в /home/hg создаем бандлы
for i in `/usr/bin/find /home/hg/ -type d -depth 1`
do
    /usr/local/bin/hg bundle -qa --cwd $i $BACKUP_DIR/hg/`basename $i`_$DATE.hg
done

HINT: Перед запуском создайте папки /var/backups/manual и /var/backups/manual/hg. Плюс к этому измените пользователя баз данных ‘backup’ на своего. Желательно /var/backups иметь на дополнительном диске, а еще лучше на другом сервере.

Блог Евгения Жирнова