Приемы написания скриптов на Bash. #2

Это пример для статьи: Приемы написания скриптов в Bash #2


Оглавление

Скрипт клонирования (синхронизации) директорий

#!/bin/bash
curScript="$0"
a1=./myfunc ; source "$a1" ; if [ $? -ne 0 ] ; then echo "Ошибка — нет библиотеки функций $a1" 1>&2 ; exit 1 ; fi

#set -x

rsync="/usr/bin/rsync" # Полный путь к команде rsync
nohup="/usr/bin/nohup" # Полный путь к команде nohup

# Объявление переменных
fixPrmCnt=0   # Счетчик фиксированных параметров
pInter=       # Интерактивный режим
pLogFile=/var/log/dir-sync.log # Имя лог-файла
pUpdate=      # Режим обновления
pShowCmd=     # Показать команду rsync
pDryRun=      # Холостой режим
pBackgr=      # Выполнять в фоне
pSrcDir=      # Директория источник
pDstDir=      # Директория приемник

RSCmd=        # Команда rsync
RSPrm=        # Дополнительные параметры rsync

# --- Создание команды rsync ---
createCmd()
{
        local n

        RSCmd="$rsync"

        if [ "$pUpdate" = "1" ] ; then
                RSCmd="$RSCmd -urlptgoDvEAH"
        else
                RSCmd="$RSCmd -rlptgoDvEAH --delete"
        fi

        # Если в фоне - не надо никакого вывода, и наоборот
        if [ "$pBackgr" = "1" ] ; then
                RSCmd="$RSCmd -q"
        else
                RSCmd="$RSCmd --progress -v"
        fi

        if [ "$pDryRun" = "1" ] ; then
                RSCmd="$RSCmd -n"
        fi

        RSCmd="$RSCmd --super --force"

        # Дополнительные параметры - элементами массива
        n=-1
        ((n++)) ; RSPrm[n]="--log-file=$pLogFile"
        ((n++)) ; RSPrm[n]="$pSrcDir/"
        ((n++)) ; RSPrm[n]="$pDstDir/"
}

usage()
{
        echo "Скрипт ${curScript##*/}"
        echo "Использование: ${curScript##*/} [ключи] <дир-источник> <дир-приемник>"
        cat <<'EOF'
Ключи:
 -i , --interactive     интерактивный режим
 -n,  --dry-run         холостое исполнение (только сообщения)
 -lf, --log-file        задает имя лог-файла (умолчание: /var/log/dir-sync.log)
 -u,  --update          режим обновления
 -sc, --show-command    показать команду rsync
 -bg, --background      выполнять в фоне (nohup)
EOF
}


showInfo()
{
        local a1

        if [ "$pUpdate" = "1" ] ; then
                a1="обновление"
        else
                a1="клонирование"
        fi

        padMid 80 "Режим" "$a1" ; echo $cRes
        padMid 80 "Источник" "$pSrcDir" ; echo $cRes
        padMid 80 "Приемник" "$pDstDir" ; echo $cRes
        padMid 80 "Лог-файл" "$pLogFile" ; echo $cRes

        transYesNoRu $pBackgr
        padMid 80 "Выполнять в фоне" "$cRes" ; echo $cRes

        transYesNoRu $pDryRun
        padMid 80 "Выполнять в холостом режиме" "$cRes" ; echo $cRes
}

if [ -z "$1" ] ; then
        usage
        exit
fi

while [ 1 ] ; do
   if [ "$1" = "--yes" ] ; then
      pYes=1
   elif [ "$1" = "-i" ] ; then
                pInter=1
   elif [ "$1" = "--interactive" ] ; then
                pInter=1
   elif procParmS "-lf" "$1" "$2" ; then
                pLogFile="$cRes" ; shift
   elif procParmL "--log-file" "$1" ; then
                pLogFile="$cRes"
   elif [ "$1" = "-u" ] ; then
                pUpdate=1
   elif [ "$1" = "--update" ] ; then
                pUpdate=1
   elif [ "$1" = "-sc" ] ; then
                pShowCmd=1
   elif [ "$1" = "--show-command" ] ; then
                pShowCmd=1
   elif [ "$1" = "-n" ] ; then
                pDryRun=1
   elif [ "$1" = "--dry-run" ] ; then
                pDryRun=1
   elif [ "$1" = "-bg" ] ; then
                pBackgr=1
   elif [ "$1" = "--background" ] ; then
                pBackgr=1
   elif [ -z "$1" ] ; then
      break # Ключи кончились
   else
      (( fixPrmCnt++ ))
      if   [ 1 -eq $fixPrmCnt ] ; then
         pSrcDir="$1"
      elif [ 2 -eq $fixPrmCnt ] ; then
         pDstDir="$1"
      else
         errMess "Ошибка: неизвестный ключ"
         exit 1
      fi
   fi
   shift
done

checkParm "$pSrcDir" "Не задана директория-источник"
checkParm "$pDstDir" "Не задана директория-приемник"

if [ "$pInter" = "1" ] && [ "$pYes" = "1" ] ; then
        errMess "Несовместимые параметры: --yes и -i"
        exit 1
fi

# Откусывыаем конечную слэш, если она задана
pSrcDir="${pSrcDir%/}"
pDstDir="${pDstDir%/}"

checkDir "$pSrcDir"
checkDir "$pDstDir"

# Если неинтерактивный запуск
if [ "$pInter" != "1" ] ; then

        # Запрос подтверждения
        if [ "$pYes" != "1" ] ; then
                echo "Скрипт ${curScript##*/} приветствует Вас!"
                showInfo
                myAskYesNo "Это может повлечь необратимые последствия! Вы уверены?" || exit
        fi

        createCmd

else
        cat <<EOF
Скрипт ${curScript##*/} приветствует Вас!
Точкой на любой вопрос Вы сможете прервать выполнение.
Выберите желаемый режим:
------------------------
   c) clone   (полное клонирование)
   u) update  (только обновление)
   .) Выход
EOF
        input1 "Твой выбор: " "cu." "dot-exit"

        pBackgr= # Чтобы не наложился параметр, заданный с командной строки
        input1 "Хотите ли Вы выполнить операцию копирования в фоне? (y/n): " "yn." "dot-exit"
        [ "$cRes" = "y" ] && pBackgr=1

        # А здесь может быть по умолчанию то, что пришло с командной строки
        read -p "Введите имя лог-файла (по умолчанию: $pLogFile): " a1
        [ -n "$a1" ] && pLogFile="$a1"

        pShowCmd= # Чтобы не наложился параметр, заданный с командной строки
        input1 "Вывести команду синхронизации на экран? (y/n): " "yn." "dot-exit"
        [ "$cRes" = "y" ] && pShowCmd=1

        createCmd

        echo
        showInfo

        if [ "$pShowCmd" = "1" ] ; then
                echo "Команда rsync:"
                echo "  $RSCmd" "${RSPrm[@]}"
        fi

        myAskYesNo "Запускаем! Вы уверены?" || exit
fi

if [ "$pBackgr" = "1" ] ; then
        nohup $RSCmd "${RSPrm[@]}" &
else
        $RSCmd "${RSPrm[@]}"
fi
К оглавлению

Библиотека функций

curPath=
cRes=
pYes=

myAskYN()
{
        local AMSURE
        if [ -n "$1" ] ; then
                read -n 1 -p "$1 (y/[a]): " AMSURE
        else
                read -n 1 AMSURE
        fi
        echo "" 1>&2
        if [ "$AMSURE" = "y" ] ; then
                return 0
        else
                return 1
        fi
}
myAskYNE()
{
        local AMSURE
        if [ -n "$1" ] ; then
                read -n 1 -p "$1 (y/[a]): " AMSURE
        else
                read -n 1 AMSURE
        fi
        echo "" 1>&2
        if [ "$AMSURE" != "y" ] ; then
                exit
        fi
}
myAskYesNo()
{
        local AMSURE
        if [ -n "$1" ] ; then
                read -p "$1 (YES/[any]): " AMSURE
        else
                read AMSURE
        fi
        if [ "$AMSURE" = "YES" ] ; then
                return 0
        else
                return 1
        fi
}
sayWait()
{
   local AMSURE
   [ -n "$1" ] && echo "$@" 1>&2
   read -n 1 -p "(нажмите любую клавишу для продолжения)" AMSURE
   echo "" 1>&2
}
cdAndCheck()
{
   cd "$1"
   if ! [ "$(pwd)" = "$1" ] ; then
      echo "!!Не могу встать в директорию $1 - продолжение невозможно. Выходим." 1>&2
      exit 1
   fi
}
checkDir()
{
   if ! [ -d "$1" ] ; then
      if [ -z "$2" ] ; then
         echo "!!Нет директории $1 - продолжение невозможно. Выходим." 1>&2
      else
         echo "$2" 1>&2
      fi
      exit 1
   fi
}
checkFile()
{
   if ! [ -f "$1" ] ; then
      if [ -z "$2" ] ; then
         echo "!!Нет файла $1 - продолжение невозможно. Выходим." 1>&2
      else
         echo "$2" 1>&2
      fi
      exit 1
   fi
}
checkParm()
{
   if [ -z "$1" ] ; then
                echo "!!$2. Продолжение невозможно. Выходим." 1>&2
      exit 1
   fi
}
input1()
{
        local a1 allowedSym

        allowedSym="$2"
        if [ "$3" = "dot-exit" ] ; then
                allowedSym="$allowedSym."
        fi

        if [ -n "$1" ] ; then
                read -p "$1" -sn 1 cRes
        else
                read -sn 1 cRes
        fi

        # Проверка допустимых выборов
        #a1="$allowedSym"
        while [ "$allowedSym" = "${allowedSym#*$cRes}" ] ; do
                read -sn 1 cRes
        done
        echo $cRes 1>&2
        if [ "$3" = "dot-exit" ] && [ "$cRes" = "." ] ; then
                exit
        fi
}
procParmS()
{
        [ -z "$2" ] && return 1
        if [ "$1" = "$2" ] ; then
                cRes="$3"
                return 0
        fi
        return 1
}
procParmL()
{
        [ -z "$1" ] && return 1
        if [ "${2#$1=}" != "$2" ] ; then
                cRes="${2#$1=}"
                return 0
        fi
        return 1
}
errMess()
{
        echo "$@" 1>&2
}
#--   padMid
# Объединение двух строк и заполнение пространства между ними точками типа:
#               Исходный файл................................myfile1.txt
#               Выходной файл................................myfile2.txt
#
# Параметры:
# 1 - Требуемый размер строки
# 2 - Первая строка
# 3 - Вторая строка
#
# Результат помещается в переменную $cRes
padMid()
{
        local len1 len2 lenDots dotStr

        len1=${#2}
        len2=${#3}
        lenDots=$1
        dotStr="................................................................................"
        ((lenDots=lenDots-len1-len2-1))
        if [ $lenDots -lt 0 ] ; then
                cRes="$2 $3"
        else
                cRes="$2${dotStr:0:$lenDots} $3"
        fi
}
outDelim80()
{
        echo "--------------------------------------------------------------------------------" 1>&2
}
transYesNoRu()
{
        if [ "$1" = "1" ] ; then
                cRes="Да"
        else
                cRes="Нет"
        fi
}
К оглавлению
  • Soft Space SIA
    Latvia
  • Этот адрес электронной почты защищен от спам-ботов. У вас должен быть включен JavaScript для просмотра.
  • +371 25 46 42 49