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