Linux 自动备份脚本

2020-04-18

写了个用于 Linux 备份的脚本。

Bash 脚本

其中几个用于配置的常量:

  • EXCLUDE:所有不备份的绝对路径,如果路径含有空格,应使用 '' 引用。

  • FILE_NAME_PREFIX:备份的文件名前缀。

  • FILE_NAME:备份的文件名(不含后缀),默认是文件名前缀+时间。

  • TEMP_PATH:临时文件(.tar)存放目录。

  • BACKUP_PATH:备份文件(.tar.xz)存放目录。

  • BACKUP_MAX:保留备份文件的最大数目,超过时将清理旧备份。

  • COPY_TO_REMOTE:是否复制到远程目录。

  • REMOTE_PATH:备份文件(.tar.xz)远程存放目录,例如使用 Rclone 挂载的路径。

linux-backup-script/backup.sh
#!/usr/bin/env bash

set -o pipefail

EXCLUDE="/dev /lost+found /media /mnt /proc /run /sys /tmp /var/cache /var/tmp /root/.cache"

FILE_NAME_PREFIX="debian"
FILE_NAME="${FILE_NAME_PREFIX}_$(date +%Y%m%d-%H%M)"

TEMP_PATH="/tmp"
BACKUP_PATH="/mnt/hdd/Backups"
BACKUP_MAX=5

COPY_TO_REMOTE=false
REMOTE_PATH="/mnt/onedrive/debian-backup"

log() {
  while read -r; do
    echo "[$(date +%F-%T)] $REPLY"
  done
}

# CHECK

[ "$(whoami)" != root ] && echo "Please run as root" | log && exit 1

[ -d $TEMP_PATH ] || (rm -f $TEMP_PATH && mkdir -p $TEMP_PATH)

[ -d $BACKUP_PATH ] || (rm -f $BACKUP_PATH && mkdir -p $BACKUP_PATH)

check_space() {
  local avail
  avail=$(df -k "$1" | sed -n "2p" | awk '{ print $4 }')
  # reserve 512MiB
  [ $((avail - $2)) -gt $((1024 * 512)) ]
}

EXCLUDE_OPT=""
for f in $EXCLUDE; do EXCLUDE_OPT="$EXCLUDE_OPT--exclude='$f' "; done

_du="du -sh $EXCLUDE_OPT /"
size=$(eval "$_du" | awk '{ print $1 }')
echo "Total size: $size" | log
_du="du -sk $EXCLUDE_OPT /"
size=$(eval "$_du" | awk '{ print $1 }')

tmp_path=$TEMP_PATH
if ! check_space $tmp_path "$size"; then
  echo "No enough space: $tmp_path" | log
  echo "Using: $BACKUP_PATH" | log
  tmp_path=$BACKUP_PATH
  if ! check_space $tmp_path "$size"; then
    echo "No enough space: $tmp_path" | log
    df -h $tmp_path | log
    exit 1
  fi
fi

# ARCHIVE

TARBALL="$tmp_path/$FILE_NAME.tar"
_tar="tar -cpf '$TARBALL' $EXCLUDE_OPT /"
echo "+ $_tar" | log
if (eval "$_tar" 2>&1) | log; then
  echo "Successfully saved as:" | log
  ls -lh "$TARBALL" | log
else
  echo "Failed to archive" | log
  rm -f "$TARBALL"
  exit 1
fi

# COMPRESS

BACKUP="$BACKUP_PATH/$FILE_NAME.tar.xz"
_xz="xz '$TARBALL' -T 0 -M 80% -c >'$BACKUP'"
echo "+ $_xz" | log
if eval "$_xz"; then
  echo "Successfully saved as:" | log
  ls -lh "$BACKUP" | log
  echo "Removing: $TARBALL" | log
  rm -f "$TARBALL"
else
  echo "Failed to compress" | log
  echo "Removing: $TARBALL" | log
  rm -f "$TARBALL"
  echo "Removing: $BACKUP" | log
  rm -f "$BACKUP"
  exit 1
fi

# REMOVE OUTDATED

count=$(find "$BACKUP_PATH" -maxdepth 1 -name "$FILE_NAME_PREFIX*.tar.xz" | wc -l)
for f in "$BACKUP_PATH/$FILE_NAME_PREFIX"*.tar.xz; do
  [ "$count" -le $BACKUP_MAX ] && break
  echo "Removing outdated: $f" | log
  rm -f "$f"
  ((count -= 1))
done

# COPY

[ $COPY_TO_REMOTE != true ] && exit 0

[ -d $REMOTE_PATH ] || (rm -f $REMOTE_PATH && mkdir -p $REMOTE_PATH)
size=$(du -sk "$BACKUP" | awk '{ print $1 }')
if ! check_space $REMOTE_PATH "$size"; then
  echo "No enough space:" | log
  df -h $REMOTE_PATH | log
  exit 1
fi

BACKUP_REMOTE="$REMOTE_PATH/$FILE_NAME.tar.xz"
echo "Copying to remote: $BACKUP_REMOTE" | log
if cp "$BACKUP" "$BACKUP_REMOTE" 2>&1 | log; then
  echo "Successfully saved as:" | log
  ls -lh "$BACKUP_REMOTE" | log
else
  echo "Failed to copy" | log
  rm -f "$BACKUP_REMOTE"
  exit 1
fi

总体过程是,先用 tar 打包存放到 TEMP_PATH,再使用 xz 压缩为最终备份文件并存放到 BACKUP_PATH,如果超出最大数则清理旧备份,最后复制到远程目录。

将脚本放到合适的路径下(如 /root/.local/bin/)并添加执行权限。

systemd 定时任务

将以下两个文件存放到 /lib/systemd/system/ 下:

linux-backup-script/backup.service
[Unit]
Description=Run backup

[Service]
ExecStart=/bin/bash -c "/root/bin/backup.sh >>/var/log/backup.log 2>&1 && echo >>/var/log/backup.log"
linux-backup-script/backup.timer
[Unit]
Description=Run backup weekly

[Timer]
OnCalendar=Mon, 03:00
AccuracySec=1h
Persistent=true

[Install]
WantedBy=timers.target

这里设置为每周一 03:00 开始备份,AccuracySec=1h 表示可以有一小时的容错,这是为了减少唤醒 CPU。

运行并使其开机启动:

$ sudo systemctl daemon-reload
$ sudo systemctl start backup.timer
$ sudo systemctl enable backup.timer

systemctl list-timers 可以查看所有定时任务。

cron 定时任务

systemd 太**难用了,还是 cron 好使,crontab -e 加入:

0 3 * * 1 /root/.local/bin/backup.sh >>/var/log/backup.log 2>&1

每周一 03:00 开始备份。

LinuxBashShell
知识共享许可协议
本作品采用知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议进行许可。

CSAPP Data Lab

缩减 IMG 镜像文件