[Из песочницы] Bash скрипт для создания архива данных

сегодня в 13:03

На днях озадачился резевным копированием данных в облако. Нашёл подходящий сервис попробовал, и понял, что существует необходимость в сжатии бэкапа перед отправкой (думаю нет необходимости объяснять зачем). Не стал заморачиваться в поиске готовых решений и решил сам написать скромный скриптик для этой цели. Исходные файл или папка жмутся в .tar.xz с уровнем сжатия 9, что позволяет сохранить права и выдаёт хорошую компрессию на выходе (у меня снэпшот системы сжимается 4 раза). Результатом остался доволен, думаю для малого бизнеса, да и для личных целей многим пригодиться.

Возможности скрипта:

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

Собственно сам скрипт (версия 2.0)
#!/bin/bash
################################################################################
##    Bash script for file or directory compression                           ##
################################################################################
## 1. Configuration variables
##  $work - Working directory. Lock and log files reside here.
##    @MUST have tail slash! @NOT mandatory!
##  $srcdir - Directory where the source resides.
##    @MUST have tail slash!
##  $srcname - Source, file or directory to compress.
##  $dest - Destination is where the archive will be placed.
##    @MUST have tail slash!
##  $archname - Archive file name.
##    [ example: "archive_$(date +"[%Y.%m.%d - %H:%M:%S]")" ]
##  $logname - Log file name.
##  $lockname - Lock file name.
##  $checklock - Check for lock file or not.
##     If you have condition(s) in which creation of an archive is not
##     recommended or fatal (e.g. while creating snapshot, directory must not
##     be tempered with in any way) and the process which is running prior to
##     archivation (rsnapshot, rsync, rdiff etc.), can create a lock file,
##     you must switch this check ON and fill out a name for a lock file,
##     to prevent integrity violation.
##     [ default: 0 ] { 0 = off, 1 = on }
##  $locksleep - How much time to wait until next try (in milliseconds).
##    [ default: 600 ] - 10 minutes.
##  $lockwait - How many tries before script stop.
##    [ default: 12 ] - Retry for 2 hours if $locksleep = 600
##  $debug - Enable debugging or not.
##  $printtype - Set output to console and or log file.
##    [ default: "l" ] { "cl" = console & log, "l" = log only }
##  $timestamp - Timestamp format.
##    [ default: "[%Y.%m.%d - %H:%M:%S]" ]
##  $TIMEFORMAT - Output format for 'time' command.
##    [ default: "%E" ]
##  $compressor - Compressor and it's params.
##    [ default: "xz -T 4 -9 -c" ]
##  $archext - Archive extension, depending on $compressor setting.
##    [ default: ".tar.xz" ]
##
## 2. Functions
##  tstamp() - Prints timestamp.
##    [ example: "$(tstamp "[%Y.%m.%d - %H:%M:%S]")" ]
##    [ fallback: "${timestamp}" ]
##  bold() - Makes text bold.
##    [ example: "$(bold "TEST")" ]
##  printout() - Prints messages.
##    [ example: printout "Archive file name is: %s\n" "$(bold $arcname)" ]
##  checkconf() - Validates configuration.
##  checklock() - Checks for lock file.
##  execcomp() - Executes compression and assigns execution time to $exectime.
##  size() - Gets object size values.
##    @Takes two parameters, object and presentation format
##      (h - human readable, b - bytes)!
##    [ example: "$(size "$src" "b")" ]
##  compratio() - Gets compression ratio.
##    @Takes two parameters, source and archive!
##    [ example: "$(compratio "$src" "$archive")" ]
##  compinf() - Outputs compression info.
##    [ example: compinf $src $archive ]
################################################################################

## =========================== ##
## Set configuration variables ##
## =========================== ##
work="/tmp/"
srcdir="/tmp/"
srcname="testfile"
dest=$work
archname=$srcname
logname="$srcname.log"
lockname="$srcname.lock"
checklock=1
locksleep=600
lockwait=12
debug=0
## Do not change this variables if you don't know how!
printtype="l"
timestamp="[%Y.%m.%d - %H:%M:%S]"
TIMEFORMAT="%E"
compressor="xz -T 4 -9 -c"
archext=".tar.xz"

## ======================================= ##
## WARNING! Do not modify past this point! ##
## ======================================= ##

## ---------------- ##
## Global variables ##
## ---------------- ##
tb=$(tput bold) ## Bold text.
tn=$(tput sgr0) ## Normal text.
src=$srcdir$srcname ## Full path to source.
archivefn=$archname$archext ## Archive file name and extension.
archive=$dest$archname$archext ## Full path to archive.
logfile=$work$logname ## Full path to log file.
lockfile=$work$lockname ## Full path to lock file.
confok=0 ## Configuration state.
execute=0 ## Allow execution.
exectime="" ## Compression execution time.

## ------------- ##
## Initial setup ##
## ------------- ##
exec 3>&1 1>>$logfile 2>&1 ## Modifies output.

## --------- ##
## Functions ##
## --------- ##
## Sets timestamp.
tstamp() { date +"${timestamp}"; }
## Makes text bold.
bold() { echo "$tb$1$tn"; }
## Prints messages.
printout() { [[ $printtype == "l" ]] && printf "$@" || (printf "$@" \
 | tee /dev/fd/3); }
## Checks configuration.
checkconf() {
  # exec 2>/dev/null
  pass=0
  checkperm() {
    if [[ ! -n ${2} ]]; then
      [[ -r ${1} ]] && [[ -w ${1} ]] && echo 1 || echo 0;
    else
      case "$2" in
        "f" ) [[ -f ${1} ]] && [[ -r ${1} ]] && [[ -w ${1} ]] && \
         echo 1 || echo 0;;
        "d" ) [[ -d ${1} ]] && [[ -r ${1} ]] && [[ -w ${1} ]] && \
         echo 1 || echo 0;;
        "fd" ) [[ -d ${1} ]] || [[ -f ${1} ]] && [[ -r ${1} ]] && \
         [[ -w ${1} ]] && echo 1 || echo 0;;
      esac
    fi
  }
  status() { [[ $1 == 1 ]] && echo "$(bold "OK")" || echo "$(bold "NOT OK")"; }
  ## Check working directory.
  status=$(status "$(checkperm "$work" "d")")
  pass=$(( pass+$(checkperm "$work" "d")))
  [[ $debug == 1 ]] && printout \
   "\t-> Working directory is %s and is set to: %s\n" "$status" "$(bold $work)"
  ## Check source.
  status=$(status "$(checkperm "$src" "fd")")
  pass=$(( pass+$(checkperm "$src" "fd")))
  [[ $debug == 1 ]] && printout "\t-> Source is %s and is set to: %s\n" \
   "$status" "$(bold $src)"
  ## Check destination.
  status=$(status "$(checkperm "$dest" "d")")
  pass=$(( pass+$(checkperm "$dest" "d")))
  [[ $debug == 1 ]] && printout "\t-> Destination is %s and is set to: %s\n" \
   "$status" "$(bold $dest)"
  ## Display rest of the config.
  [[ $debug == 1 ]] && printout "\t-> Archive is set to: %s\n" \
   "$(bold $archivefn)"
  [[ $debug == 1 ]] && printout "\t-> Log file is set to: %s\n" \
   "$(bold $logname)"
  [[ $debug == 1 ]] && [[ $checklock == 1 ]] && printout \
  "\t-> Lock file is set to: %s\n" "$(bold $lockname)"
  [[ $debug == 1 ]] && printout "\t-> Timestamp is set to: %s\n" \
   "$(bold "$timestamp")"
  [[ $debug == 1 ]] && printout "\t-> Compressor is set to: %s\n" \
   "$(bold "$compressor")"
  ## Validate config
  if [ $pass == 3 ]; then
    confok=1; [[ $debug == 1 ]] && printout "%s Configuration is %s!\n" \
     "$(tstamp)" "$(bold "OK")"
  else
    printout "%s Configuration is %s, exiting...\n" "$(tstamp)" \
     "$(bold "NOT OK")"; exit 1
  fi
}
## Checks for lock file.
checklock() {
  for ((i=1; i<=$lockwait; i++)); do
    if [ -f ${lockfile} ]; then
      [[ $debug == 1 ]] && printout \
       "%s Lock file is in place, waiting... %s\n" "$(tstamp)" \
        "$(bold "$i")"; sleep $locksleep
    else
      i=1000
    fi
    if [ $i == $lockwait ]; then
      printout "%s Lock file is in place for to long, exiting...\n\n" "$(tstamp)"
      exit 0
    fi
  done
}
## Executes compression.
execcomp() {
  cd $srcdir || exit 1 ## Go to archive destination directory.
  exectime=$( { time tar cf - $srcname | $compressor - > $archive; } 2>&1 )
}
## Gets object size values.
size() {
  [[ $2 == "b" ]] && du -bs "$1" | awk '{ print $1 }'
  [[ $2 == "h" ]] && du -hs "$1" | awk '{ print $1 }'
}
## Gets compression ratio.
compratio() {
  res=$(let res="$1/$2"; printf "%s" "$res"); printf "%.*f" 2 "$res"
}
## Outputs compression info.
compinf() {
  printout "\t-> Source size: %s (%s bytes)\n" \
   "$(bold "$(size "$1" "h")")" "$(bold "$(size "$1" "b")")"
  printout "\t-> Archive size: %s (%s bytes)\n" \
   "$(bold "$(size "$2" "h")")" "$(bold "$(size "$2" "b")")"
  printout "\t-> Compression ratio: %s\n" \
   "$(bold "$(compratio "$(size "$1" "b")" "$(size "$2" "b")")")"
}

## ---------- ##
## Initialize ##
## ---------- ##
printout "%s Initializing...\n" "$(tstamp)"
checkconf ## Check configuration.
[[ $checklock == 1 ]] && checklock ## Check for lock file.

## Check if the old archive file exists, if it does, delete it.
if [ -f ${archive} ]; then
  [[ $debug == 1 ]] && printout "%s Old archive exists, deleting...\n" \
   "$(tstamp)"; rm -f ${archive}
  if [ ! -f ${archive} ]; then
    execute=1; [[ $debug == 1 ]] && printout \
     "%s Old archive deleted, proceeding...\n" "$(tstamp)"
  else
    printout "%s %s Can't delete old archive file, exiting...\n" \
     "$(tstamp)" "$(bold "WARNING!")"; exit 1
  fi
else
  execute=1; [[ $debug == 1 ]] && printout \
   "%s Old archive does not exist, proceeding...\n" "$(tstamp)"
fi

## Begin compression.
if [ $execute == 1 ]; then
  [[ $debug == 1 ]] && printout "%s Flag is set, executing...\n" \
   "$(tstamp)"; execcomp
  ## Check if archive file created.
  if [ -f ${archive} ]; then
    printout "%s Archive %s created in %s!\n" "$(tstamp)" "$(bold $archive)" \
     "$(bold ${exectime})"
    compinf $src $archive
    # printout "%s Process has completed successfully!\n" "$(tstamp)"
  else
    printout "%s %s Archive file was NOT created, exiting...\n" \
     "$(tstamp)" "$(bold "WARNING!")"; exit 1
  fi
else
  printout "%s Flag is NOT set, exiting...\n" "$(tstamp)"; exit 1
fi

printout "\n"; exit 0



P.S.
Тем, кому пригодился и есть желание доработать или сделать более элегантным, прошу на GitHub.

stub-user-middle.gif

© Habrahabr.ru