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_FILEexit 1导致脚本退出。

我们不知道你的logging命令是做什么的,但据我所知,它并不是要写入$LOG_FILE.如果是的话,这样写就有点傻了检查日志文件:${LOG_FILE}那里。

编辑

现在您已经发布了该logging函数,我可以看到它使用了此处字符串 ( <<<)。

在 中bash,here-strings 和 here-documents 是使用临时文件实现的(在$TMPDIR/tmpif中$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 文件系统的系统是一个瘫痪的系统,您可以预料到很多事情都无法正常工作。

在这里,你很幸运你有日志。文件的磁盘空间是以块的形式分配的(在 ext4 上通常为 4K),这可能就是为什么你在 `$LOG_FILE 中得到一些输出(因为最后一个块是在文件系统变满之前分配的)。

由 cron 运行的脚本也将其 stdout 和 stderr 放在临时文件上(如果它们不为空,则 cron 会尝试发送包含其内容的电子邮件)。因此,任何命令也可能会write(1, ...)失败write(2, ...)(带有 ENOSPC 错误),如果它们认为这是致命错误,则可能会导致它们行为不当或提前退出。

答案2

问题很有可能是

PACKING_EXITCODE=${PIPESTATUS[0]}

不是有效的 shell 代码,而是bash特定的代码。

Cron 调用与/bin/sh不同的命令bash

你可以让你的脚本开始

#!/bin/bash

并使脚本可执行,chmod +x scriptname以确保bash特定代码由默认 shell 执行bash,而不是由默认 shell 执行。

相关内容