После сбоя tar скрипт завершает работу без обработки ошибки.

Итак, у меня есть скрипт резервного копирования, который выглядит следующим образом:

tar -cf "${BACKUP_TAR}" "${LATEST_SUCCESSFUL_BACKUP}" 2>&1 | tee -a "${LOG_FILE}" local PACKING_EXITCODE=${PIPESTATUS[0]} if [ ${PACKING_EXITCODE} -eq 0 ]; then logging 'Packing successful' else logging "ERROR: Packing failed! ERROR: ${PACKING_EXITCODE}. Disk space?" df -h 2>&1 | tee -a "${LOG_FILE}" logging "Check the log file: ${LOG_FILE}" set_lockfile 'destroy' backup_remove_package exit 1 fi 

logging – это функция для правильного входа в мой файл журнала.

 logging () { local now="$(date)" local logfile=$2 local logfile=${logfile:-$LOG_FILE} cat <<< "${now} $@" | tee -a "${logfile}" } 

set_lockfile “destroy” `- это функция, которая удаляет мой файл блокировки.

 set_lockfile () { local lockfile_action=$1 local lockfile=$2 local lockfile=${lockfile:-$LOCK_FILE} if [ "${lockfile_action}" == "create" ]; then #... elif [ "${lockfile_action}" == "destroy" ]; then destroy_lockfile $lockfile else logging 'ERROR: Wrong argument for locking file: use create or destroy' exit 1 fi } destroy_lockfile () { local lockfile=$1 if [ ! -f ${lockfile} ]; then logging "WARNING: Lockfile ${lockfile} not found!" else logging "Removing lockfile ${lockfile}" rm -f "${lockfile}" fi } 

backup_remove_package – это функция для удаления любых созданных временных файлов.

Я испытываю сбой упаковки из-за переполнения диска, ожидаемое поведение, как вы можете догадаться для df -h .

Интересная вещь – это журнал резервного копирования. Говорится:

 tar: /tmp/backup/20180827T223001.tar: Wrote only 4096 of 10240 bytes tar: Error is not recoverable: exiting now Filesystem Size Used Avail Use% Mounted on /dev/xvda1 788G 788G 0 100% / devtmpfs 3.9G 60K 3.9G 1% /dev tmpfs 3.9G 0 3.9G 0% /dev/shm 

Это означает, что tar потерпел неудачу, затем он прошел через условие if , каким-то образом пропустил logging "ERROR: ..." , выполнил df -h и умер. пропуская остальные.

Похоже, что пропускает любую функцию, кроме запуска команд.

Резервная копия вызывается из файла cron.d Я НЕ установил set -e , поэтому нет выхода при ошибке.

Есть идеи, почему это происходит?

Ваш сценарий работает так, как ожидалось. Вывод df явно $LOG_FILE в $LOG_FILE а exit 1 вызывает завершение работы скрипта.

Мы не знаем, что делает ваша команда logging , но, к слову, она не предназначена для записи в $LOG_FILE . Если бы это было так, было бы немного глупо писать. Проверьте файл журнала: $ {LOG_FILE} там.

редактировать

Теперь, когда вы опубликовали функцию logging , я вижу, что она использует строку здесь ( <<< ).

В bash here-строки и here-документы реализуются с использованием временных файлов (в $TMPDIR или /tmp если $TMPDIR не определен). Если это была файловая система, которая была заполнена, это объясняло бы, почему logging ничего не выводила.

 $ sudo mount -o size=1 -t tmpfs empty /mnt/1 $ yes > /mnt/1/fill-up yes: standard output: No space left on device $ TMPDIR=/mnt/1 bash -c 'cat <<< test' bash: cannot create temp file for here-document: No space left on device 

Вместо:

 local now="$(date)" cat <<< "${now} $@" | tee -a "${logfile}" 

Просто используйте:

 printf '%(%FT%T%z)T %s\n' -1 "$*" printf '%(%FT%T%z)T %s\n' -1 "$*" >> "$logfile" 

Или же:

 local msg printf -v msg '%(%FT%T%z)T %s' -1 "$*" printf '%s\n' "$msg" printf '%s\n' "$msg" >> "$logfile" 

(предполагается, что $IFS не установлен или начинается с пробела)

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

В более общем смысле, система с файловой системой full / tmp и / var является поврежденной системой, вы можете ожидать, что многие вещи будут работать неправильно.

Здесь вам повезло, что у вас есть логи вообще. Дисковое пространство для файлов распределяется в блоках (обычно 4K на ext4), поэтому, вероятно, вы получили некоторый вывод в `$ LOG_FILE (так как последний блок был выделен до того, как файловая система была заполнена).

Скрипты, запускаемые cron, также имеют свои stdout и stderr во временном файле (затем cron пытается отправить электронное письмо со своим содержимым, если они не пусты). Таким образом, любая из команд может также потерпеть неудачу при write(1, ...) или write(2, ...) (с ошибкой ENOSPC), что может привести к неправильному поведению или преждевременному завершению работы, если они считают это фатальной ошибкой.

Существует высокая вероятность того, что проблема заключается в том, что

 PACKING_EXITCODE=${PIPESTATUS[0]} 

не является допустимым кодом оболочки, но является специфическим для bash .

Cron вызывает команды с /bin/sh который отличается от bash .

Вы можете позволить вашему сценарию начать с

 #!/bin/bash 

и сделать сценарий исполняемым, используя chmod +x scriptname чтобы убедиться, что специфичный для bash код выполняется bash а не оболочкой по умолчанию