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

После сбоя 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, поэтому выход при ошибке не выполняется.

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

решение1

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

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

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

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

В bashhere-strings и here-documents реализованы с использованием временных файлов (в $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что не установлено или начинается с пробела)

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

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

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

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

решение2

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

PACKING_EXITCODE=${PIPESTATUS[0]}

это не действительный шелл-код, а нечто конкретное bash.

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

Вы могли бы начать свой сценарий с

#!/bin/bash

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

Связанный контент