Приемы написания скриптов на 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
}

