Вопрос или проблема
Я пытаюсь запустить скрипт, который вызывает другой скрипт, но, похоже, он не возвращается к исходному скрипту, который его вызвал.
Исходный скрипт “backup-plex.sh” вызывает “Linux_Plex_Backup.sh” (передавая аргумент для пути назначения).
После завершения работы скрипта “Linux_Plex_Backup.sh” он должен вернуться к исходному скрипту “backup-plex.sh” и продолжить с командой rsync, но этого не происходит.
Я вызываю исходный скрипт с помощью:
~/.backupScripts/./backup-plex.sh
Ниже приведен исходный скрипт, который вызывает скрипт “Linux_Plex_Backup.sh”:
#!/bin/bash
# Определить исходный путь
source=/media/plex1
# Определить пути назначения
destination=/media/backPlex1ON
dest_plex_library=PlexLibraryBackup
echo "********************* Выполнение резервного копирования медиатеки Plex *********************"
# вызов скрипта резервного копирования медиатеки Plex
sudo -s ~/.backupScripts/PlexBackup/Linux_Plex_Backup.sh $destination/$dest_plex_library
echo "********************* Выполнение резервного копирования Plex 1 с помощью rsync *********************"
# исключения для команды rsync
rsync_excludes="--exclude '*.ini' \
--exclude '*.DS_Store' \
--exclude '*._*' \
--exclude '**/\$RECYCLE.BIN' \
--exclude '**/\lost+found' \
--exclude '*.AppleDouble' \
--exclude '*.localized' \
--exclude '*.Trash-1000' \
--exclude '**/\PlexLibraryBackup'"
# параметры команды rsync
rsync_cmd="/usr/local/bin/rsync"
rsync_options="-a --progress -l --delete $rsync_excludes"
eval $rsync_cmd $rsync_options $source $destination
Ниже приведен скрипт “Linux_Plex_Backup.sh”, вызываемый исходным скриптом…
#!/usr/bin/env bash
# shellcheck disable=SC2317,SC2181
#--------------------------------------------------------------------------
# Резервное копирование базы данных Plex Linux в tgz-файл в папке резервного копирования.
# v1.1.6 04-Nov-2024 007revad
#
# Должен выполняться пользователем из группы sudo, sudoers или wheel, или как root
#
# Для запуска скрипта:
# sudo -i /share/scripts/backup_linux_plex_to_tar.sh
# Измените /share/scripts/ на путь, где находится этот скрипт
#
# Чтобы выполнить тестовый запуск только на папке профилей Plex, выполните:
# sudo -i /share/scripts/backup_linux_plex_to_tar.sh test
# Измените /share/scripts/ на путь, где находится этот скрипт
#
# Github: https://github.com/007revad/Linux_Plex_Backup
# Скрипт проверен на https://www.shellcheck.net/
#--------------------------------------------------------------------------
scriptver="v1.1.6"
script=Linux_Plex_Backup
if [ -z $1 ] ; then
echo "Отсутствует аргумент: определите путь назначения"
exit
fi
# Чтение переменных из backup_linux_plex.config
Backup_Directory="$1" # JL: директория для резервного копирования передается в качестве параметра ($1) при вызове скрипта Linux_Plex_Backup
Name=""
LogAll=""
KeepQty=""
if [[ -f $(dirname -- "$0";)/backup_linux_plex.config ]];then
# shellcheck disable=SC1090,SC1091
while read -r var; do
if [[ $var =~ ^[a-zA-Z0-9_]+=.* ]]; then export "$var"; fi
done < "$(dirname -- "$0";)"/backup_linux_plex.config
else
echo "Файл backup_linux_plex.config отсутствует!"
exit 1
fi
# Проверка существования директории для резервного копирования
if [[ ! -d $Backup_Directory ]]; then
echo "Директория для резервного копирования не найдена:"
echo "$Backup_Directory"
echo "Проверьте настройки в backup_linux_plex.config"
exit 1
fi
#--------------------------------------------------------------------------
# Установить переменные даты и времени
# Переменная для таймера для регистрации времени, затраченного на резервное копирование PMS
start="${SECONDS}"
# Получить время и дату начала
Started=$( date )
# Получить сегодняшнюю дату для имени файла
Now=$( date '+%Y%m%d')
# Получить сегодняшнюю дату и время для имени файла в случае, если файл существует
NowLong=$( date '+%Y%m%d-%H%M')
#--------------------------------------------------------------------------
# Установить имя NAS (используется в именах файлов резервного копирования и логов)
case "${Name,,}" in
distro)
# Получить дистрибутив Linux
Nas="$(uname -a | awk '{print $2}')"
;;
hostname|"")
# Получить имя хоста
Nas=$( hostname )
;;
*)
# Установить Nas на псевдоним
Nas="$Name"
;;
esac
#--------------------------------------------------------------------------
# Установить временные имена файлов логов (версию Plex получаем позже)
# Установить имя файла резервного копирования
Backup_Name="${Nas}"_"${Now}"_Plex_"${Version}"_Backup
# Если файл уже существует, включить время в имя
BackupPN="$Backup_Directory/$Backup_Name"
if [[ -f $BackupPN.tgz ]] || [[ -f $BackupPN.log ]] || [[ -f "$BackupPN"_ERROR.log ]]; then
Backup_Name="${Nas}"_"${NowLong}"_Plex_"${Version}"_Backup
fi
# Установить имя файла лога
Log_File="${Backup_Directory}"/"${Backup_Name}".log
# Установить имя файла ошибок
Err_Log_File="${Backup_Directory}"/"${Backup_Name}"_ERROR.log
#--------------------------------------------------------------------------
# Создать временный файл лога ошибок
# Создать временную директорию для временного файла лога ошибок
Tmp_Dir=$(mktemp -d -t plex_to_tar-XXXXXX)
# Создать временный файл лога ошибок
Tmp_Err_Log_File=$(mktemp "${Tmp_Dir}"/errorlog-XXXXXX)
#--------------------------------------------------------------------------
# Создать ловушку и функцию очистки
# Функция очистки временных логов
# shellcheck disable=SC2329
cleanup(){
arg1=$?
# Переместить tmp_error_log в лог ошибок, если tmp_error_log не пуст
if [[ -s $Tmp_Err_Log_File ]] && [[ -d $Backup_Directory ]]; then
mv "${Tmp_Err_Log_File}" "${Err_Log_File}"
if [[ $? -gt "0" ]]; then
echo "ВНИМАНИЕ Не удалось переместить ${Tmp_Err_Log_File} в ${Err_Log_File}"\
|& tee -a "${Err_Log_File}"
fi
fi
# Удалить нашу временную директорию
if [[ -d $Tmp_Dir ]]; then
rm -rf "${Tmp_Dir}"
if [[ $? -gt "0" ]]; then
echo "ВНИМАНИЕ Не удалось удалить ${Tmp_Dir}" |& tee -a "${Err_Log_File}"
fi
fi
if [[ $Version ]]; then Version="${Version} "; fi
# Регистрация и уведомление о успешности или ошибках
if [[ -f $Err_Log_File ]]; then
# Регистрация и уведомление о наличии ошибок в резервном копировании
if [[ ! -f $Log_File ]]; then
# Добавить имя скрипта в начало файла лога
basename -- "$0" |& tee -a "${Log_File}"
fi
echo -e "\n\e[41mВНИМАНИЕ\e[0m В резервном копировании Plex возникли ошибки! Смотрите лог ошибок:"
echo -e "\nВНИМАНИЕ В резервном копировании Plex возникли ошибки! Смотрите лог ошибок:" >> "${Log_File}"
echo -e "$(basename -- "${Err_Log_File}")\n" |& tee -a "${Log_File}"
else
# Регистрация и уведомление о успешном резервном копировании
echo -e "\nРезервное копирование Plex выполнено успешно" |& tee -a "${Log_File}"
fi
exit "${arg1}"
}
trap cleanup EXIT
#--------------------------------------------------------------------------
# Проверка, что скрипт выполняется от имени root
if [[ $( whoami ) != "root" ]]; then
if [[ -d $Backup_Directory ]]; then
echo "ОШИБКА: Этот скрипт должен выполняться от имени root!" |& tee -a "${Tmp_Err_Log_File}"
echo "ОШИБКА: $( whoami ) не является root. Прерывание." |& tee -a "${Tmp_Err_Log_File}"
else
# Невозможно записать ошибку в файл лога, так как $Backup_Directory не существует
echo -e "\nОШИБКА: Этот скрипт должен выполняться от имени root!"
echo -e "ОШИБКА: $( whoami ) не является root. Прерывание.\n"
fi
# Прерывание скрипта, так как он не выполняется от имени root
exit 255
fi
#--------------------------------------------------------------------------
# Расположение папки "Plex Media Server"
# ADM /volume1/Plex/Library/Plex Media Server
# DSM6 /volume1/Plex/Library/Application Support/Plex Media Server
# DSM7 /volume1/PlexMediaServer/AppData/Plex Media Server
# Linux /var/lib/plexmediaserver/Library/Application Support/Plex Media Server
# Установить расположение данных Plex Media Server
Plex_Data_Path="/var/lib/plexmediaserver/Library/Application Support"
#--------------------------------------------------------------------------
# Проверка существования пути данных Plex Media Server
if [[ ! -d $Plex_Data_Path ]]; then
echo "Недействительный путь данных Plex Media Server! Прерывание." |& tee -a "${Tmp_Err_Log_File}"
echo "${Plex_Data_Path}" |& tee -a "${Tmp_Err_Log_File}"
# Прерывание скрипта, так как путь данных Plex недействителен
exit 255
fi
#--------------------------------------------------------------------------
# Получение версии Plex Media Server
Version="$(/usr/lib/plexmediaserver/Plex\ Media\ Server --version)"
# Возвращает v1.29.2.6364-6d72b0cf6
# Версия Plex без v или шестнадцатеричной строки
Version=$(printf %s "${Version:1}"| cut -d "-" -f1)
# Возвращает 1.29.2.6364
#--------------------------------------------------------------------------
# Переопределение имен логов для включения версии Plex
# Имя файла резервного копирования
Backup_Name="${Nas}"_"${Now}"_Plex_"${Version}"_Backup
# Если файл уже существует, включить время в имя
BackupPN="$Backup_Directory/$Backup_Name"
if [[ -f $BackupPN.tgz ]] || [[ -f $BackupPN.log ]] || [[ -f "$BackupPN"_ERROR.log ]]; then
Backup_Name="${Nas}"_"${NowLong}"_Plex_"${Version}"_Backup
fi
# Имя файла лога
Log_File="${Backup_Directory}"/"${Backup_Name}".log
# Имя файла ошибок
Err_Log_File="${Backup_Directory}"/"${Backup_Name}"_ERROR.log
#--------------------------------------------------------------------------
# Начало логирования
echo -e "$script $scriptver\n" |& tee -a "${Log_File}"
# Логирование дистрибутива, версии и имени хоста Linux
Distro="$(uname -a | awk '{print $2}')"
DistroVersion="$(uname -a | awk '{print $3}' | cut -d"-" -f1)"
echo "${Distro}" "${DistroVersion}" |& tee -a "${Log_File}"
echo "Имя хоста: $( hostname )" |& tee -a "${Log_File}"
# Логирование версии Plex
echo Версия Plex: "${Version}" |& tee -a "${Log_File}"
#--------------------------------------------------------------------------
# Проверка существования директории для резервного копирования
if [[ ! -d $Backup_Directory ]]; then
echo "ОШИБКА: Директория для резервного копирования не найдена! Прерывание резервного копирования." |& tee -a "${Log_File}" "${Tmp_Err_Log_File}"
# Прерывание скрипта, так как директория для резервного копирования не найдена
exit 255
fi
#--------------------------------------------------------------------------
# Остановка Plex Media Server
echo "Остановка Plex..." |& tee -a "${Log_File}"
Result=$(systemctl stop plexmediaserver)
code="$?"
# Дать сокетам немного времени, чтобы закрыться
sleep 5
if [[ $code == "0" ]]; then
echo "Plex Media Server остановлен." |& tee -a "$Log_File"
else
echo "$Result" |& tee -a "$Log_File"
exit $code
fi
# Аккуратное завершение любых оставшихся процессов Plex (плагины, служба тюнера и EAE и т.д.)
###pgrep [Pp]lex | xargs kill -15 &>/dev/null
# Дать сокетам немного времени, чтобы закрыться
###sleep 5
# Убить любые оставшиеся процессы, которые DSM не очистил (плагины и EAE)
Pids="$(ps -ef | grep -i 'plex plug-in' | grep -v grep | awk '{print $2}')"
[ "$Pids" != "" ] && kill -9 "$Pids"
Pids="$(ps -ef | grep -i 'plex eae service' | grep -v grep | awk '{print $2}')"
[ "$Pids" != "" ] && kill -9 "$Pids"
Pids="$(ps -ef | grep -i 'plex tuner service' | grep -v grep | awk '{print $2}')"
[ "$Pids" != "" ] && kill -9 "$Pids"
# Дать сокетам немного времени, чтобы закрыться
sleep 2
#--------------------------------------------------------------------------
# Проверка остановки всех процессов Plex
echo Проверка состояния процессов Plex... |& tee -a "${Log_File}"
Response=$(pgrep -l plex)
# Проверка, найден ли plexmediaserver в $Response
if [[ -n $Response ]]; then
# Принудительное завершение любых оставшихся процессов Plex (плагины, служба тюнера и EAE и т.д.)
pgrep [Pp]lex | xargs kill -9 &>/dev/null
sleep 5
# Проверка, найден ли plexmediaserver все еще в $Response
Response=$(pgrep -l plex)
if [[ -n $Response ]]; then
echo "ОШИБКА: Некоторые процессы Plex все еще работают! Прерывание резервного копирования."\
|& tee -a "${Log_File}" "${Tmp_Err_Log_File}"
echo "${Response}" |& tee -a "${Log_File}" "${Tmp_Err_Log_File}"
# Запуск Plex для уверенности, что он не остался частично запущенным
/usr/lib/plexmediaserver/Resources/start.sh
# Прерывание скрипта, так как Plex полностью не завершился
exit 255
else
echo "Все процессы Plex остановлены." |& tee -a "${Log_File}"
fi
else
echo "Все процессы Plex остановлены." |& tee -a "${Log_File}"
fi
#--------------------------------------------------------------------------
# Резервное копирование Plex Media Server
echo "=================================================" |& tee -a "${Log_File}"
echo "Резервное копирование файлов данных Plex Media Server..." |& tee -a "${Log_File}"
Exclude_File="$( dirname -- "$0"; )/plex_backup_exclude.txt"
# Проверка аргументов теста или ошибки
if [[ -n $1 ]] && [[ ${1,,} == "error" ]]; then
# Вызов ошибки для тестирования логов ошибок
Test="Plex Media Server/Logs/ERROR/"
echo "Запуск небольшого теста резервного копирования папки журналов" |& tee -a "${Log_File}"
elif [[ -n $1 ]] && [[ ${1,,} == "test" ]]; then
# Тестирование на небольшой папке журналов
Test="Plex Media Server/Logs/"
echo "Запуск небольшого теста резервного копирования папки журналов" |& tee -a "${Log_File}"
fi
# Проверка наличия файла исключения
# Должен следовать за "Проверка аргументов теста или ошибки"
if [[ -f $Exclude_File ]]; then
# Сброс аргументов
while [[ $1 ]]; do shift; done
# Установка аргументов -X excludefile для tar
set -- "$@" "-X"
set -- "$@" "${Exclude_File}"
else
echo "ИНФОРМАЦИЯ: Файл исключения отсутствует." |& tee -a "${Log_File}"
fi
# Использовать короткие имена переменных, чтобы команда tar не была слишком длинной
BD="${Backup_Directory}"
BN="${Backup_Name}"
PDP="${Plex_Data_Path}"
LF="${Log_File}"
TELF="${Tmp_Err_Log_File}"
PMS="Plex Media Server"
# Выполнить команду tar для резервного копирования
if [[ -n $Test ]]; then
# Запуск теста резервного копирования или теста ошибки
if [[ ${LogAll,,} == "yes" ]]; then
echo "Запись всех архивированных файлов" |& tee -a "${Log_File}"
tar -cvpzf "${BD}"/"${BN}".tgz -C "${PDP}" "${Test}" > >(tee -a "${LF}") 2> >(tee -a "${LF}" "${TELF}" >&2)
else
# Не регистрировать все резервные копии.
echo "Только регистрация ошибок" |& tee -a "${Log_File}"
tar -cvpzf "${BD}"/"${BN}".tgz -C "${PDP}" "${Test}" 2> >(tee -a "${LF}" "${TELF}" >&2)
fi
else
# Резервное копирование в tgz с версией PMS и датой в имени файла, отправка всего вывода в оболочку и лог, плюс ошибки в error.log
# Использование -C для смены каталога на "/share/Plex/Library/Application Support" для того, чтобы не резервировать абсолю пути
# и избежать ошибки "tar: Removing leading /"
if [[ ${LogAll,,} == "yes" ]]; then
echo "Запись всех архивированных файлов" |& tee -a "${Log_File}"
tar -cvpzf "${BD}"/"${BN}".tgz "$@" -C "${PDP}" "$PMS/" > >(tee -a "${LF}") 2> >(tee -a "${LF}" "${TELF}" >&2)
else
# Не регистрировать все резервные копии.
echo "Только регистрация ошибок" |& tee -a "${Log_File}"
tar -cvpzf "${BD}"/"${BN}".tgz "$@" -C "${PDP}" "$PMS/" 2> >(tee -a "${LF}" "${TELF}" >&2)
fi
fi
echo "Завершено резервное копирование файлов данных Plex Media Server." |& tee -a "${Log_File}"
echo "=================================================" |& tee -a "${Log_File}"
#--------------------------------------------------------------------------
# Запуск Plex Media Server
echo "Запуск Plex..." |& tee -a "${Log_File}"
#/usr/lib/plexmediaserver/Resources/start.sh
systemctl start plexmediaserver
#--------------------------------------------------------------------------
# Удаление старых резервных копий
if [[ $KeepQty -gt "0" ]]; then
readarray -t array < <(ls "$Backup_Directory" |\
grep -E "${Nas}"'_[0-9]{8,}(-[0-9]{4,})?_Plex_.*\.tgz' | head -n -"$KeepQty")
if [[ "${#array[@]}" -gt "0" ]]; then
echo -e "\nУдаление старых резервных копий" |& tee -a "${Log_File}"
for file in "${array[@]}"; do
if [[ -f "$Backup_Directory/$file" ]]; then
echo "Удаление $file" |& tee -a "${Log_File}"
rm "$Backup_Directory/$file"
fi
if [[ -f "$Backup_Directory/${file%.tgz}.log" ]]; then
echo "Удаление ${file%.tgz}.log" |& tee -a "${Log_File}"
rm "$Backup_Directory/${file%.tgz}.log"
fi
if [[ -f "$Backup_Directory/${file%.tgz}_ERROR.log" ]]; then
echo "Удаление ${file%.tgz}_ERROR.log" |& tee -a "${Log_File}"
rm "$Backup_Directory/${file%.tgz}_ERROR.log"
fi
done
fi
fi
#--------------------------------------------------------------------------
# Добавление времени выполнения к stdout и файлу логов
# Время и дата окончания
Finished=$( date )
# Переменная времени Bash для регистрации времени, затраченного на резервное копирование Plex
end="${SECONDS}"
# Время выполнения в секундах
Runtime=$(( end - start ))
# Добавление времени и даты начала и окончания, а также времени выполнения
echo -e "\nРезервное копирование начато: " "${Started}" |& tee -a "${Log_File}"
echo "Резервное копирование завершено:" "${Finished}" |& tee -a "${Log_File}"
# Добавление дней, часов, минут и секунд из $Runtime
printf "Продолжительность резервного копирования: " |& tee -a "${Log_File}"
printf '%dd:%02dh:%02dm:%02ds\n' \
$((Runtime/86400)) $((Runtime%86400/3600)) $((Runtime%3600/60))\
$((Runtime%60)) |& tee -a "${Log_File}"
#--------------------------------------------------------------------------
# Вызов функции очистки
exit 0
Исправления, которые я внес и почему, прокомментированы в коде ниже. Попробуйте, если проблема сохраняется, пожалуйста, ответьте, и я постараюсь адаптировать исправление.
#!/bin/bash
# Определить исходный путь
source=/media/plex1
# Определить пути назначения
destination=/media/backPlex1ON # Исправлена опечатка: изменено '==' на '='
dest_plex_library=PlexLibraryBackup
echo "********************* Выполнение резервного копирования медиатеки Plex *********************"
# Вызов скрипта резервного копирования медиатеки Plex с sudo
# Команда sudo -s не является необходимой и может быть причиной проблемы. Вместо этого используйте sudo непосредственно для запуска скрипта.
sudo ~/.backupScripts/PlexBackup/Linux_Plex_Backup.sh $destination/$dest_plex_library
echo "********************* Выполнение резервного копирования Plex 1 с помощью rsync *********************"
# исключения для команды rsync
# Используйте двойные кавычки для переменной rsync_excludes и убедитесь, что команда eval правильно отформатирована.
rsync_excludes="--exclude '*.ini' \
--exclude '*.DS_Store' \
--exclude '*._*' \
--exclude '**/\$RECYCLE.BIN' \
--exclude '**/lost+found' \
--exclude '*.AppleDouble' \
--exclude '*.localized' \
--exclude '*.Trash-1000' \
--exclude '**/PlexLibraryBackup'"
# Определение команды и параметров rsync
rsync_cmd="/usr/local/bin/rsync"
rsync_options="-a --progress -l --delete $rsync_excludes"
# Выполнение команды rsync с помощью eval для включения всех параметров и путей
eval $rsync_cmd $rsync_options $source $destination
Надеюсь, это поможет!
Ответ или решение
Если вы столкнулись с проблемой преждевременного завершения выполнения Bash-скрипта при вызове другого скрипта внутри него, возможно, причина в нескольких аспектах настройки и использования команд. Давайте рассмотрим возможные решения данной проблемы с использованием приведенного примера скрипта.
Проблема
Вы запускаете основной скрипт backup-plex.sh
, который вызывает другой скрипт Linux_Plex_Backup.sh
для выполнения резервного копирования. Однако, после завершения работы второго скрипта, оригинальный скрипт не продолжает выполнение команды rsync
, как ожидается.
Причины и решения
-
Использование
sudo -s
:В вашем основном скрипте используется команда
sudo -s
, чтобы переключиться на пользователя root и запустить скриптLinux_Plex_Backup.sh
. Эта команда может вызывать проблемы из-за дополнительной инстанции оболочки, которая может завершить выполнение всего процесса. Вместо этого используйте толькоsudo
:sudo ~/.backupScripts/PlexBackup/Linux_Plex_Backup.sh $destination/$dest_plex_library
-
Проблемы с переменными:
В вашем скрипте есть ошибка синтаксиса – двойное равно (
==
), которое используется для присваивания. Замените на одно равно:destination=/media/backPlex1ON
-
Проверка завершения второго скрипта:
Убедитесь, что
Linux_Plex_Backup.sh
завершает выполнение корректно и возвращает код завершения 0. Если этот скрипт завершает работу с ошибкой (код отличен от 0), основной скрипт не сможет продолжить. Проверьте наличие командыexit
вLinux_Plex_Backup.sh
, которая используется для выхода с указанием кода ошибки. -
Логирование и отладка:
Включите логирование и отладку внутри обоих скриптов. Это можно сделать добавлением флага
-x
в начале скрипта, чтобы видеть ход выполнения команд:#!/bin/bash -x
Логирование может помочь выявить конкретную точку, на которой выполнение скрипта останавливается.
-
Отсутствие конфигурационного файла:
Убедитесь, что все требуемые файлы, такие как
backup_linux_plex.config
, существуют и доступны для чтения. Отсутствие необходимых файлов может привести к преждевременному завершению работы. -
Команды
rsync
иeval
:Убедитесь, что команды
rsync
правильно сформированы и передаются. Используйте правильное экранирование и кавычки вокруг включенных путей или опций, чтобы избежать ошибок.
Заключение
Проверьте вышеуказанные аспекты и внесите соответствующие правки в ваш скрипт. Надеюсь, это поможет вам решить проблему преждевременного завершения скрипта. Если проблема сохраняется, проведите дополнительную диагностику, возможно понадобится более детальное логирование или отладка на уровне низкоуровневых команд.